Revert "Delete git-eb, it's obsolete"
[sunny256-utils.git] / mktar
blob4e04d3a819bf67c7cea8caf40d4bb74368f38ae6
1 #!/usr/bin/env bash
3 #=======================================================================
4 # mktar
5 # File ID: 5818b856-0ba9-11de-b2c1-000475e441b9
7 # Move a whole subdirectory tree into a single .tar.gz file.
9 # Author: Øyvind A. Holm <sunny@sunbase.org>
10 # License: GNU General Public License version 2 or later.
11 #=======================================================================
13 progname=mktar
14 VERSION=0.28.2
16 STD_SUFFIX=''
18 ARGS="$(getopt -o "\
28 o:\
29 P:\
32 S:\
33 s:\
38 " -l "\
39 dirs-only,\
40 force,\
41 help,\
42 incremental,\
43 dereference,\
44 random-mac,\
45 no-git-check,\
46 no-actime,\
47 no-uuid,\
48 no-xattrs,\
49 numeric-owner,\
50 output-dir:,\
51 prefix:,\
52 quiet,\
53 remove-files,\
54 suffix:,\
55 split:,\
56 verbose,\
57 version,\
58 xz,\
59 gzip,\
60 " -n "$progname" -- "$@")"
61 test "$?" = "0" || exit 1
62 eval set -- "$ARGS"
64 opt_1=0
65 opt_no_git_check=0
66 opt_dirs_only=0
67 opt_force=0
68 opt_help=0
69 opt_incremental=0
70 opt_dereference=0
71 opt_random_mac=0
72 opt_no_uuid=0
73 opt_no_actime=0
74 opt_no_xattrs=0
75 opt_numeric_owner=0
76 opt_output_dir=''
77 opt_prefix=''
78 opt_quiet=0
79 opt_remove_files=0
80 opt_suffix="$STD_SUFFIX"
81 opt_split=''
82 opt_verbose=0
83 opt_xz=0
84 opt_gzip=0
85 while :; do
86 case "$1" in
87 -1) opt_1=1; shift ;;
88 -G|--no-git-check) opt_no_git_check=1; shift ;;
89 -d|--dirs-only) opt_dirs_only=1; shift ;;
90 -f|--force) opt_force=1; shift ;;
91 -h|--help) opt_help=1; shift ;;
92 -i|--incremental) opt_incremental=1; shift ;;
93 -L|--dereference) opt_dereference=1; shift ;;
94 -m|--random-mac) opt_random_mac=1; shift ;;
95 --no-uuid) opt_no_uuid=1; shift ;;
96 --numeric-owner) opt_numeric_owner=1; shift ;;
97 -o|--output-dir) opt_output_dir="$2"; shift 2 ;;
98 -P|--prefix) opt_prefix="$2"; shift 2 ;;
99 -q|--quiet) opt_quiet=$(($opt_quiet + 1)); shift ;;
100 -r|--remove-files) opt_remove_files=1; shift ;;
101 -S|--suffix) opt_suffix="$2"; shift 2 ;;
102 -s|--split) opt_split="$2"; shift 2 ;;
103 -v|--verbose) opt_verbose=$(($opt_verbose + 1)); shift ;;
104 --version) echo $progname $VERSION; exit 0 ;;
105 -A|--no-actime) opt_no_actime=1; shift ;;
106 -X|--no-xattrs) opt_no_xattrs=1; shift ;;
107 -x|--xz) opt_xz=1; shift ;;
108 -z|--gzip) opt_gzip=1; shift ;;
109 --) shift; break ;;
110 *) echo $progname: Internal error >&2; exit 1 ;;
111 esac
112 done
113 opt_verbose=$(($opt_verbose - $opt_quiet))
115 if test "$opt_help" = "1"; then
116 test $opt_verbose -gt 0 && { echo; echo $progname $VERSION; }
117 cat <<END
119 Move a whole subdirectory tree into a single $STD_SUFFIX.tar.gz file.
121 Usage: $progname [options] DIRECTORY [DIRECTORIES [...]]
123 Options:
126 Alias for "--split 1G". No file systems should have problems with
127 this size, and it makes it easy to calculate the size of the .tar
128 file.
129 -d, --dirs-only
130 Ignore non-directory arguments.
131 -f, --force
132 Don't abort if the .tar file exists, delete it before proceeding.
133 -G, --no-git-check
134 To protect against potential data loss, it first loops through all
135 directories to check that no files under the directories are stored
136 in a parent Git repository. If any files are stored under any of the
137 directories, it aborts. It is useful for example with git-annex,
138 where only a symlink would be stored in the archive file. This
139 option disables the check.
140 -h, --help
141 Show this help.
142 -i, --incremental
143 Create a .snar file with information for creating incremental
144 backups. Uses the -g/--listed-incremental option in GNU tar.
145 -L, --dereference
146 Don't pack symlinks, include the actual files they point to.
147 -m, --random-mac
148 Use random MAC address in UUID file label.
149 --no-uuid
150 Don't create UUID label in the .tar file.
151 --numeric-owner
152 Use numbers for user/group names.
153 -o DIR, --output-dir DIR
154 Store the tar files in DIR instead of the current directory.
155 -P PREFIX, --prefix PREFIX
156 Use PREFIX at the beginning of the tar filenames. Doesn't change the
157 name of the extracted files. Adds a terminating '.' if it doesn't
158 exist.
159 -q, --quiet
160 Be more quiet. Can be repeated to increase silence.
161 -r, --remove-files
162 Remove files in DIRECTORY immediately after they've been added to
163 the archive. Can be used when there's not enough disk space for the
164 archive.
165 -S SUFFIX, --suffix SUFFIX
166 Add a custom suffix after the directory name and before the .tar
167 extension in the output filename. No changes are made to the
168 resulting tar file, only the file name is different. An intial '.'
169 is added if it's missing.
170 Default value: "$STD_SUFFIX".
171 -s SIZE, --split SIZE
172 Split the .tar file into files with SIZE bytes each. Allowed values
173 are those understood by the -b/--bytes option in split(1). These
174 files are not compressed by default, to make it easier to extract
175 data from the files without starting from the beginning.
176 -v, --verbose
177 Increase level of verbosity. Can be repeated.
178 --version
179 Print version information.
180 -A, --no-actime
181 Don't store atime or ctime in the tar file. Use with --no-uuid to
182 generate identical tar files.
183 -X, --no-xattrs
184 Don't use --xattrs with tar(1), create standard tar archive without
185 extended attributes and nanoseconds.
186 -x, --xz
187 Compress the archives with xz(1) after the files are added.
188 -z, --gzip
189 Compress the archives with gzip(1) after the files are added.
192 exit 0
195 if test "$opt_xz" = "1" -a "$opt_gzip" = "1"; then
196 echo $progname: Cannot mix the --gzip and --xz options >&2
197 exit 1
200 if test "$opt_1" = "1" -a -n "$opt_split"; then
201 echo $progname: Cannot mix the -1 and --split options >&2
202 exit 1
205 test "$opt_1" = "1" && opt_split=1G
207 if test "$opt_dereference" = "1"; then
208 dereference_str="--dereference"
209 else
210 dereference_str=""
213 if test "$opt_no_actime" = "1"; then
214 actime_str="--pax-option=delete=atime,delete=ctime"
215 else
216 actime_str=""
219 if test "$opt_random_mac" = "1"; then
220 random_mac_str="--random-mac"
221 else
222 random_mac_str=""
225 if test "$opt_remove_files" = "1"; then
226 rm_files_str="--remove-files"
227 else
228 rm_files_str=""
231 if test "$opt_no_xattrs" = "1"; then
232 xattrs_str=""
233 else
234 xattrs_str="--xattrs"
237 if test "$opt_numeric_owner" = "1"; then
238 numeric_str="--numeric-owner"
239 else
240 numeric_str=""
243 for f in "$@"; do
244 if test -d "$f" -o "$opt_dirs_only" != "1"; then
245 if test "$opt_no_git_check" != "1"; then
246 git ls-files "$f" | grep -q . >&2 && {
247 echo -n "$progname: $f: " >&2
248 if test -d "$f"; then
249 echo Files are stored in Git below this directory >&2
250 else
251 echo File is stored in Git >&2
253 echo $progname: Use -G/--no-git-check to add it anyway >&2
254 exit 1
257 else
258 echo $progname: $f: Not a directory >&2
260 done
262 prefix="$opt_prefix"
263 if test -n "$prefix"; then
264 echo "$prefix" | grep -q '\.$' || prefix="$prefix."
267 suffix="$opt_suffix"
268 if test -n "$suffix"; then
269 echo "$suffix" | grep -q '^\.' || suffix=".$suffix"
272 if test -n "$opt_output_dir"; then
273 if test ! -d "$opt_output_dir/."; then
274 echo "$progname: $opt_output_dir: Directory not found" >&2
275 exit 1
277 output_dir="$opt_output_dir/"
278 else
279 output_dir="";
282 for f in "$@"; do
283 dir="$(echo -n "$f" | sed 's/\/*$//')"
284 if [ -z "$dir" ]; then
285 echo "$progname: $f: Argument is empty after stripping slash" >&2
286 exit 1
288 tarfile="$output_dir$prefix$dir$suffix.tar"
290 if test $opt_verbose -ge 2; then
291 echo $progname: dir = $dir >&2
294 if [ -d "$dir" -o "$opt_dirs_only" != "1" ]; then
295 echo >&2
296 echo $progname: Packing $dir... >&2
298 if ls "$tarfile"* 2>/dev/null | grep -q .; then
299 if test "$opt_force" = "1"; then
300 rm -fv "$tarfile"*
301 else
302 echo "$progname: $tarfile* already exist" >&2
303 exit 1
307 echo "$dir" | grep -q / && {
308 echo "$progname: $dir: Slashes not allowed in the file name" >&2
309 exit 1
312 if test "$opt_no_uuid" = "1"; then
313 label_str=""
314 else
315 uuid=$(
316 suuid -t mktar --raw -w eo $random_mac_str \
317 -c "<c_mktar> <filename>$tarfile</filename> <host>$(
318 hostname
319 )</host> <directory>$(/bin/pwd)</directory> </c_mktar>"
320 ) || {
321 echo $progname: suuid error >&2
322 exit 1
324 label_str="--label=$uuid"
326 if test "$opt_incremental" = "1"; then
327 incremental_str="--listed-incremental $prefix$dir$suffix.snar"
328 else
329 incremental_str=""
331 if test -n "$opt_split"; then
332 echo $progname: tar c $incremental_str $rm_files_str \
333 --force-local $actime_str --sort=name --sparse \
334 $dereference_str $numeric_str $xattrs_str $dir \| \
335 split -b $opt_split --verbose - $tarfile.split_ >&2
336 tar c $incremental_str $rm_files_str \
337 --force-local $actime_str --sort=name --sparse \
338 $dereference_str $numeric_str $xattrs_str \
339 $label_str "$dir" |
340 split -b $opt_split --verbose - "$tarfile.split_" || exit 1
341 if test ! -e "$tarfile.split_ab"; then
342 mv -vi "$tarfile.split_aa" "$tarfile" || exit 1
344 test "$opt_xz" = "1" && xz -v "$tarfile"*
345 test "$opt_gzip" = "1" && gzip -vn --rsyncable "$tarfile"*
346 else
347 echo $progname: tar cf $tarfile $incremental_str $rm_files_str \
348 --force-local $actime_str --sort=name --sparse \
349 $dereference_str $numeric_str $xattrs_str $dir >&2
350 tar cf "$tarfile" $incremental_str $rm_files_str \
351 --force-local $actime_str --sort=name --sparse \
352 $dereference_str $numeric_str $xattrs_str \
353 $label_str "$dir" || exit 1
354 ls -la "$tarfile" >&2
355 test "$opt_xz" = "1" && xz -v "$tarfile"
356 test "$opt_gzip" = "1" && gzip -vn --rsyncable "$tarfile"
359 done
361 exit 0