implement multiple archive creation
This commit is contained in:
@@ -115,9 +115,11 @@ utaz()
|
|||||||
# shellcheck disable=SC2329
|
# shellcheck disable=SC2329
|
||||||
_unlha()
|
_unlha()
|
||||||
{
|
{
|
||||||
# lha typically extracts into the current directory
|
# lha extracts into the current directory, so we cd into the target
|
||||||
# We ensure it hits the target directory
|
# directory and feed it an absolute path to the archive.
|
||||||
(cd "$2" && lha -x "../$1") >/dev/null 2>&1
|
local src
|
||||||
|
src=$(realpath -- "$1") || return 1
|
||||||
|
(cd "$2" && lha -x "$src") >/dev/null 2>&1
|
||||||
}
|
}
|
||||||
|
|
||||||
local _ununace
|
local _ununace
|
||||||
@@ -146,8 +148,11 @@ utaz()
|
|||||||
# shellcheck disable=SC2329
|
# shellcheck disable=SC2329
|
||||||
_uncpio()
|
_uncpio()
|
||||||
{
|
{
|
||||||
# CPIO requires careful directory handling
|
# cpio extracts into the current directory, so we cd into the target
|
||||||
(cd "$2" && cpio -id < "../$1") >/dev/null 2>&1
|
# directory and read from an absolute path to the archive.
|
||||||
|
local src
|
||||||
|
src=$(realpath -- "$1") || return 1
|
||||||
|
(cd "$2" && cpio -id < "$src") >/dev/null 2>&1
|
||||||
}
|
}
|
||||||
|
|
||||||
local _uncabextract
|
local _uncabextract
|
||||||
@@ -249,8 +254,10 @@ utaz()
|
|||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
[[ -n ${createdir} && -n ${nodir} ]] && \
|
[[ -n ${createdir} && -n ${nodir} ]] && {
|
||||||
disp E "The --create-dir and --no-dir options are mutually exclusive."
|
disp E "The --create-dir and --no-dir options are mutually exclusive."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
for zitem in "${FILES[@]}"; do
|
for zitem in "${FILES[@]}"; do
|
||||||
# Build list of input files to process, with whitespace-safe handling.
|
# Build list of input files to process, with whitespace-safe handling.
|
||||||
@@ -353,6 +360,10 @@ utaz()
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
disp I "Processing archive ${f} with ${extractor}..."
|
disp I "Processing archive ${f} with ${extractor}..."
|
||||||
|
# Track whether we create the target directory so a corrupted
|
||||||
|
# archive never wipes a pre-existing directory of the same name.
|
||||||
|
local dir_created=0
|
||||||
|
[[ -d "${dir}" ]] || dir_created=1
|
||||||
if ! mkdir -p "${dir}"; then
|
if ! mkdir -p "${dir}"; then
|
||||||
disp E "The filesystem can't create directories, exit!" &&
|
disp E "The filesystem can't create directories, exit!" &&
|
||||||
return 1
|
return 1
|
||||||
@@ -373,7 +384,7 @@ utaz()
|
|||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
disp E "The compressed file ${f} seems corrupted, failed."
|
disp E "The compressed file ${f} seems corrupted, failed."
|
||||||
rm -rf "${dir}" >/dev/null 2>&1
|
[[ ${dir_created} -eq 1 ]] && rm -rf "${dir}" >/dev/null 2>&1
|
||||||
continue
|
continue
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@@ -444,6 +455,17 @@ taz()
|
|||||||
printf "%s\n" "$cpus"
|
printf "%s\n" "$cpus"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Render a byte count as a human-readable size (B/KB/MB/GB/...), base 1024.
|
||||||
|
local _taz_human
|
||||||
|
# shellcheck disable=SC2329
|
||||||
|
_taz_human()
|
||||||
|
{
|
||||||
|
if command -v numfmt >/dev/null 2>&1; then
|
||||||
|
numfmt --to=iec --suffix=B -- "$1" 2>/dev/null && return 0
|
||||||
|
fi
|
||||||
|
printf "%sB\n" "$1"
|
||||||
|
}
|
||||||
|
|
||||||
local _doxz
|
local _doxz
|
||||||
# shellcheck disable=SC2329
|
# shellcheck disable=SC2329
|
||||||
_doxz()
|
_doxz()
|
||||||
@@ -575,6 +597,7 @@ taz()
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
eval set -- "$PARSED"
|
eval set -- "$PARSED"
|
||||||
|
local compforms=()
|
||||||
while true; do
|
while true; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
-h|--help)
|
-h|--help)
|
||||||
@@ -608,7 +631,12 @@ taz()
|
|||||||
;;
|
;;
|
||||||
|
|
||||||
-f|--format)
|
-f|--format)
|
||||||
local compform=$2
|
# Accept comma/space separated lists and repeated -f flags.
|
||||||
|
local _f
|
||||||
|
# shellcheck disable=SC2086 # intentional word splitting on the list
|
||||||
|
for _f in ${2//,/ }; do
|
||||||
|
compforms+=("$_f")
|
||||||
|
done
|
||||||
shift 2
|
shift 2
|
||||||
;;
|
;;
|
||||||
|
|
||||||
@@ -628,7 +656,7 @@ taz()
|
|||||||
;;
|
;;
|
||||||
|
|
||||||
-[1-9])
|
-[1-9])
|
||||||
complevel="${1#-}"
|
local complevel="${1#-}"
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
--)
|
--)
|
||||||
@@ -647,11 +675,32 @@ taz()
|
|||||||
local FILES=("$@")
|
local FILES=("$@")
|
||||||
[[ ${#FILES[@]} -eq 0 ]] && FILES=(".")
|
[[ ${#FILES[@]} -eq 0 ]] && FILES=(".")
|
||||||
|
|
||||||
[[ ! $compform ]] && compform=${TAZ_DEFAULT_FORMAT:-lz}
|
[[ ! $compform ]] && local compform=${TAZ_DEFAULT_FORMAT:-lz}
|
||||||
[[ ! $nproc ]] && nproc=${TAZ_DEFAULT_THREADS:-auto}
|
[[ ! $nproc ]] && local nproc=${TAZ_DEFAULT_THREADS:-auto}
|
||||||
[[ ! $complevel ]] && complevel=${TAZ_DEFAULT_LEVEL:-6}
|
[[ ! $complevel ]] && local complevel=${TAZ_DEFAULT_LEVEL:-6}
|
||||||
[[ $verbose -gt 1 && $quiet -gt 1 ]] &&
|
[[ -n $verbose && -n $quiet ]] && {
|
||||||
disp E "The --verbose and --quiet options can't be used together."
|
disp E "The --verbose and --quiet options can't be used together."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Fall back to the configured (or built-in) default format list.
|
||||||
|
local _f
|
||||||
|
if [[ ${#compforms[@]} -eq 0 ]]; then
|
||||||
|
local _deffmt=${TAZ_DEFAULT_FORMAT:-lz}
|
||||||
|
# shellcheck disable=SC2086 # intentional word splitting on the list
|
||||||
|
for _f in ${_deffmt//,/ }; do
|
||||||
|
compforms+=("$_f")
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Reject unknown formats early so we never dispatch to a missing _do*.
|
||||||
|
local _supported=" lz xz bz2 gz lzo tar "
|
||||||
|
for _f in "${compforms[@]}"; do
|
||||||
|
[[ $_supported == *" $_f "* ]] || {
|
||||||
|
disp E "Unsupported format '${_f}' (supported: lz xz bz2 gz lzo tar)."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
done
|
||||||
|
|
||||||
# Backward compatibility: 0 previously meant auto-detect.
|
# Backward compatibility: 0 previously meant auto-detect.
|
||||||
[[ $nproc == 0 ]] && nproc=auto
|
[[ $nproc == 0 ]] && nproc=auto
|
||||||
@@ -662,6 +711,10 @@ taz()
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Map each format to the extension its compressor appends. "tar" performs
|
||||||
|
# no compression, so its candidate is the tar (or source file) itself.
|
||||||
|
local -A _taz_ext=( [lz]=lz [xz]=xz [bz2]=bz2 [gz]=gz [lzo]=lzo [tar]="" )
|
||||||
|
|
||||||
for item in "${FILES[@]}"; do
|
for item in "${FILES[@]}"; do
|
||||||
local donetar=0
|
local donetar=0
|
||||||
disp I "Processing $item..."
|
disp I "Processing $item..."
|
||||||
@@ -680,31 +733,91 @@ taz()
|
|||||||
local fname=$item
|
local fname=$item
|
||||||
[[ $donetar -gt 0 ]] && fname=$item.tar
|
[[ $donetar -gt 0 ]] && fname=$item.tar
|
||||||
|
|
||||||
# Skip compression part if tar is asked
|
# Compress the archive with every requested format; the smallest
|
||||||
if [[ $compform != "tar" ]]; then
|
# resulting file is kept and the others are discarded.
|
||||||
disp I "\t Compressing archive..."
|
local candidates=() fmt exec_code aborted=0
|
||||||
local exec_code=0
|
for fmt in "${compforms[@]}"; do
|
||||||
"_do$compform" "$fname" "$nproc" "$complevel" "$verbose" || exec_code=$?
|
if [[ $fmt == tar ]]; then
|
||||||
[[ ! $exec_code -eq 0 ]] && case $exec_code in
|
candidates+=("$fname")
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
disp I "\t Compressing archive with ${fmt}..."
|
||||||
|
exec_code=0
|
||||||
|
"_do$fmt" "$fname" "$nproc" "$complevel" "$verbose" || exec_code=$?
|
||||||
|
case $exec_code in
|
||||||
|
0)
|
||||||
|
candidates+=("$fname.${_taz_ext[$fmt]}")
|
||||||
|
;;
|
||||||
127)
|
127)
|
||||||
disp E "Compression program unavailable, aborting."
|
disp E "Compression program for '${fmt}' unavailable, aborting."
|
||||||
return 127
|
aborted=1
|
||||||
|
break
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
disp E "Compression program returned an error, not deleting anything if asked, skipping to next item."
|
disp W "Compression with '${fmt}' failed, skipping this format."
|
||||||
continue
|
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
[[ $donetar -gt 0 ]] && rm "$fname"
|
# Remove partial output produced before an unrecoverable abort.
|
||||||
|
local c
|
||||||
|
if [[ $aborted -eq 1 ]]; then
|
||||||
|
for c in "${candidates[@]}"; do
|
||||||
|
[[ $c != "$fname" ]] && rm -f -- "$c"
|
||||||
|
done
|
||||||
|
[[ $donetar -gt 0 ]] && rm -f -- "$fname"
|
||||||
|
return 127
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Select the smallest archive among the produced candidates.
|
||||||
|
local winner="" winner_size=-1 sz
|
||||||
|
local -A _csize=()
|
||||||
|
for c in "${candidates[@]}"; do
|
||||||
|
[[ -f $c ]] || continue
|
||||||
|
sz=$(stat -c%s -- "$c" 2>/dev/null) || continue
|
||||||
|
_csize["$c"]=$sz
|
||||||
|
if [[ $winner_size -lt 0 || $sz -lt $winner_size ]]; then
|
||||||
|
winner_size=$sz
|
||||||
|
winner=$c
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -z $winner ]]; then
|
||||||
|
disp E "No archive could be produced for ${item}, skipping to next item."
|
||||||
|
[[ $donetar -gt 0 ]] && rm -f -- "$fname"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# In verbose mode, list every produced archive with its size and how
|
||||||
|
# much larger it is than the smallest one that will be kept.
|
||||||
|
if [[ -n $verbose && ${#_csize[@]} -gt 0 ]]; then
|
||||||
|
disp I "\t Produced archives:"
|
||||||
|
for c in "${candidates[@]}"; do
|
||||||
|
[[ -n ${_csize[$c]+x} ]] || continue
|
||||||
|
if [[ $c == "$winner" ]]; then
|
||||||
|
disp "\t ${c} $(_taz_human "${_csize[$c]}") (kept, smallest)"
|
||||||
|
else
|
||||||
|
disp "\t ${c} $(_taz_human "${_csize[$c]}") (+$(_taz_human "$(( _csize[$c] - winner_size ))"))"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Discard every archive but the smallest, then drop the intermediate
|
||||||
|
# tar unless it is the kept archive.
|
||||||
|
for c in "${candidates[@]}"; do
|
||||||
|
[[ $c != "$winner" ]] && rm -f -- "$c"
|
||||||
|
done
|
||||||
|
[[ $donetar -gt 0 && $fname != "$winner" ]] && rm -f -- "$fname"
|
||||||
|
|
||||||
|
[[ ${#compforms[@]} -gt 1 ]] &&
|
||||||
|
disp I "\t Kept smallest archive: ${winner} ($(_taz_human "$winner_size"))."
|
||||||
|
|
||||||
if [[ $willrm ]]; then
|
if [[ $willrm ]]; then
|
||||||
disp I "\t Deleting original source as asked... "
|
disp I "\t Deleting original source as asked... "
|
||||||
rm -r "$item"
|
rm -r "$item"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
unset quiet
|
|
||||||
}
|
}
|
||||||
export -f taz
|
export -f taz
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user