Delete obsolete Unicode version 15.1.0
[sunny256-utils.git] / mktar
blob159bbf90d93041cf3e50173912b1e931e3070611
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.29.3
16 STD_SUFFIX=''
18 ARGS="$(getopt -o "\
29 o:\
30 P:\
33 S:\
34 s:\
39 " -l "\
40 dirs-only,\
41 force,\
42 help,\
43 incremental,\
44 dereference,\
45 random-mac,\
46 no-git-check,\
47 no-actime,\
48 no-uuid,\
49 no-xattrs,\
50 numeric-owner,\
51 output-dir:,\
52 prefix:,\
53 quiet,\
54 remove-files,\
55 suffix:,\
56 split:,\
57 stdout,\
58 verbose,\
59 version,\
60 xz,\
61 gzip,\
62 " -n "$progname" -- "$@")"
63 test "$?" = "0" || exit 1
64 eval set -- "$ARGS"
66 opt_1=0
67 opt_no_git_check=0
68 opt_dirs_only=0
69 opt_force=0
70 opt_help=0
71 opt_incremental=0
72 opt_dereference=0
73 opt_random_mac=0
74 opt_no_uuid=0
75 opt_no_actime=0
76 opt_no_xattrs=0
77 opt_numeric_owner=0
78 opt_output_dir=''
79 opt_prefix=''
80 opt_quiet=0
81 opt_remove_files=0
82 opt_suffix="$STD_SUFFIX"
83 opt_split=''
84 opt_stdout=0
85 opt_verbose=0
86 opt_xz=0
87 opt_gzip=0
88 while :; do
89 case "$1" in
90 -1) opt_1=1; shift ;;
91 -G|--no-git-check) opt_no_git_check=1; shift ;;
92 -d|--dirs-only) opt_dirs_only=1; shift ;;
93 -f|--force) opt_force=1; shift ;;
94 -h|--help) opt_help=1; shift ;;
95 -i|--incremental) opt_incremental=1; shift ;;
96 -L|--dereference) opt_dereference=1; shift ;;
97 -m|--random-mac) opt_random_mac=1; shift ;;
98 --no-uuid) opt_no_uuid=1; shift ;;
99 --numeric-owner) opt_numeric_owner=1; shift ;;
100 -o|--output-dir) opt_output_dir="$2"; shift 2 ;;
101 -P|--prefix) opt_prefix="$2"; shift 2 ;;
102 -q|--quiet) opt_quiet=$(($opt_quiet + 1)); shift ;;
103 -r|--remove-files) opt_remove_files=1; shift ;;
104 -S|--suffix) opt_suffix="$2"; shift 2 ;;
105 -s|--split) opt_split="$2"; shift 2 ;;
106 -O|--stdout) opt_stdout=1; shift ;;
107 -v|--verbose) opt_verbose=$(($opt_verbose + 1)); shift ;;
108 --version) echo $progname $VERSION; exit 0 ;;
109 -A|--no-actime) opt_no_actime=1; shift ;;
110 -X|--no-xattrs) opt_no_xattrs=1; shift ;;
111 -x|--xz) opt_xz=1; shift ;;
112 -z|--gzip) opt_gzip=1; shift ;;
113 --) shift; break ;;
114 *) echo $progname: Internal error >&2; exit 1 ;;
115 esac
116 done
117 opt_verbose=$(($opt_verbose - $opt_quiet))
119 if [ -n "$POSIXLY_CORRECT" ]; then
120 tput bold
121 tput setaf 1
122 echo $progname: POSIXLY_CORRECT = \"$POSIXLY_CORRECT\" >&2
123 tput sgr0
126 if test "$opt_help" = "1"; then
127 test $opt_verbose -gt 0 && { echo; echo $progname $VERSION; }
128 cat <<END
130 Move a whole subdirectory tree into a single $STD_SUFFIX.tar.gz file.
132 Usage: $progname [options] DIRECTORY [DIRECTORIES [...]]
134 Options:
137 Alias for "--split 1G". No file systems should have problems with
138 this size, and it makes it easy to calculate the size of the .tar
139 file.
140 -d, --dirs-only
141 Ignore non-directory arguments.
142 -f, --force
143 Don't abort if the .tar file exists, delete it before proceeding.
144 -G, --no-git-check
145 To protect against potential data loss, it first loops through all
146 arguments to check that none of them are stored in Git. If any files
147 are stored under any of the directories, it aborts. Useful with for
148 example git-annex, where only a symlink would be stored in the
149 archive file. This option disables the check.
150 -h, --help
151 Show this help.
152 -i, --incremental
153 Create a .snar file with information for creating incremental
154 backups. Uses the -g/--listed-incremental option in GNU tar.
155 -L, --dereference
156 Don't pack symlinks, include the actual files they point to.
157 -m, --random-mac
158 Use random MAC address in UUID file label.
159 --no-uuid
160 Don't create UUID label in the .tar file.
161 --numeric-owner
162 Use numbers for user/group names.
163 -O, --stdout
164 Send the .tar output to stdout instead of creating files on disk.
165 All nonsensical options are ignored, for example --split. Does not
166 work with compression.
167 -o DIR, --output-dir DIR
168 Store the tar files in DIR instead of the current directory.
169 -P PREFIX, --prefix PREFIX
170 Use PREFIX at the beginning of the tar filenames. Doesn't change the
171 name of the extracted files. Adds a terminating '.' if it doesn't
172 exist.
173 -q, --quiet
174 Be more quiet. Can be repeated to increase silence.
175 -r, --remove-files
176 Remove files in DIRECTORY immediately after they've been added to
177 the archive. Can be used when there's not enough disk space for the
178 archive.
179 -S SUFFIX, --suffix SUFFIX
180 Add a custom suffix after the directory name and before the .tar
181 extension in the output filename. No changes are made to the
182 resulting tar file, only the file name is different. An intial '.'
183 is added if it's missing.
184 Default value: "$STD_SUFFIX".
185 -s SIZE, --split SIZE
186 Split the .tar file into files with SIZE bytes each. Allowed values
187 are those understood by the -b/--bytes option in split(1). These
188 files are not compressed by default, to make it easier to extract
189 data from the files without starting from the beginning.
190 -v, --verbose
191 Increase level of verbosity. Can be repeated.
192 --version
193 Print version information.
194 -A, --no-actime
195 Don't store atime or ctime in the tar file. Use with --no-uuid to
196 generate identical tar files.
197 -X, --no-xattrs
198 Don't use --xattrs with tar(1), create standard tar archive without
199 extended attributes and nanoseconds.
200 -x, --xz
201 Compress the archives with xz(1) after the files are added.
202 -z, --gzip
203 Compress the archives with gzip(1) after the files are added.
206 exit 0
209 if test "$opt_xz" = "1" -a "$opt_gzip" = "1"; then
210 echo $progname: Cannot mix the --gzip and --xz options >&2
211 exit 1
214 if test "$opt_1" = "1" -a -n "$opt_split"; then
215 echo $progname: Cannot mix the -1 and --split options >&2
216 exit 1
219 if test "$opt_stdout" = "1" -a -n "$(echo $opt_gzip$opt_xz | grep 1)"; then
220 echo $progname: Cannot use compression with -O/--stdout >&2
221 exit 1
224 test "$opt_1" = "1" && opt_split=1G
226 if test "$opt_dereference" = "1"; then
227 dereference_str="--dereference"
228 else
229 dereference_str=""
232 if test "$opt_no_actime" = "1"; then
233 actime_str="--pax-option=delete=atime,delete=ctime"
234 else
235 actime_str=""
238 if test "$opt_random_mac" = "1"; then
239 random_mac_str="--random-mac"
240 else
241 random_mac_str=""
244 if test "$opt_remove_files" = "1"; then
245 rm_files_str="--remove-files"
246 else
247 rm_files_str=""
250 if test "$opt_no_xattrs" = "1"; then
251 xattrs_str=""
252 else
253 xattrs_str="--xattrs"
256 if test "$opt_numeric_owner" = "1"; then
257 numeric_str="--numeric-owner"
258 else
259 numeric_str=""
262 for f in "$@"; do
263 if test -d "$f" -o "$opt_dirs_only" != "1"; then
264 if test "$opt_no_git_check" != "1"; then
265 git ls-files "$f" | grep -q . >&2 && {
266 echo -n "$progname: $f: " >&2
267 if test -d "$f"; then
268 echo Files are stored in Git below this directory >&2
269 else
270 echo File is stored in Git >&2
272 echo $progname: Use -G/--no-git-check to add it anyway >&2
273 exit 1
276 else
277 echo $progname: $f: Not a directory >&2
279 done
281 prefix="$opt_prefix"
282 if test -n "$prefix"; then
283 echo "$prefix" | grep -q '\.$' || prefix="$prefix."
286 suffix="$opt_suffix"
287 if test -n "$suffix"; then
288 echo "$suffix" | grep -q '^\.' || suffix=".$suffix"
291 if test -n "$opt_output_dir"; then
292 if test ! -d "$opt_output_dir/."; then
293 echo "$progname: $opt_output_dir: Directory not found" >&2
294 exit 1
296 output_dir="$opt_output_dir/"
297 else
298 output_dir="";
301 for f in "$@"; do
302 dir="$(echo -n "$f" | sed 's/\/*$//')"
303 if [ -z "$dir" ]; then
304 echo "$progname: $f: Argument is empty after stripping slash" >&2
305 exit 1
307 tarfile="$output_dir$prefix$dir$suffix.tar"
309 if test $opt_verbose -ge 2; then
310 echo $progname: dir = $dir >&2
313 if [ -d "$dir" -o "$opt_dirs_only" != "1" ]; then
314 echo >&2
315 echo $progname: Packing $dir... >&2
317 if test "$opt_stdout" != "1"; then
318 if ls "$tarfile"* 2>/dev/null | grep -q .; then
319 if test "$opt_force" = "1"; then
320 rm -fv "$tarfile"*
321 else
322 echo "$progname: $tarfile* already exist" >&2
323 exit 1
328 echo "$dir" | grep -q / && {
329 echo "$progname: $dir: Slashes not allowed in the file name" >&2
330 exit 1
333 if test "$opt_no_uuid" = "1"; then
334 label_str=""
335 else
336 uuid=$(
337 suuid -t mktar --raw -w eo $random_mac_str \
338 -c "<c_mktar> <filename>$tarfile</filename> <host>$(
339 hostname
340 )</host> <directory>$(/bin/pwd)</directory> </c_mktar>"
341 ) || {
342 echo $progname: suuid error >&2
343 exit 1
345 label_str="--label=$uuid"
347 if test "$opt_incremental" = "1"; then
348 incremental_str="--listed-incremental $prefix$dir$suffix.snar"
349 else
350 incremental_str=""
352 tar_args="$incremental_str $rm_files_str"
353 tar_args="$tar_args --force-local $actime_str --sort=name --sparse"
354 tar_args="$tar_args $dereference_str $numeric_str $xattrs_str"
355 tar_args="$tar_args $label_str"
356 if test "$opt_stdout" = "1"; then
357 echo $progname: Sending to stdout >&2
358 echo $progname: tar c $tar_args $dir >&2
359 tar c $tar_args "$dir" || exit 1
360 continue
362 if test -n "$opt_split"; then
363 echo $progname: tar c $tar_args $dir \
364 \| split -b $opt_split --verbose - $tarfile.split_ >&2
365 tar c $tar_args "$dir" \
366 | split -b $opt_split --verbose - "$tarfile.split_" || exit 1
367 if test ! -e "$tarfile.split_ab"; then
368 mv -vi "$tarfile.split_aa" "$tarfile" || exit 1
370 test "$opt_xz" = "1" && xz -v "$tarfile"*
371 test "$opt_gzip" = "1" && gzip -vn --rsyncable "$tarfile"*
372 else
373 echo $progname: tar cf $tarfile $tar_args $dir >&2
374 tar cf "$tarfile" $tar_args "$dir" || exit 1
375 if test "$opt_stdout" != "1"; then
376 wc -c "$tarfile" \
377 | numfmt --to=si --format=%7.2f --round=nearest >&2
378 test "$opt_xz" = "1" && xz -v "$tarfile"
379 test "$opt_gzip" = "1" && gzip -vn --rsyncable "$tarfile"
383 done
385 exit 0