#!/usr/bin/env bash # ------------------------------------------------------------------------------ # Copyright (c) 2013-2022 Geoffray Levasseur # Protected by the BSD3 license. Please read bellow for details. # # * Redistribution and use in source and binary forms, # * with or without modification, are permitted provided # * that the following conditions are met: # * # * Redistributions of source code must retain the above # * copyright notice, this list of conditions and the # * following disclaimer. # * # * Redistributions in binary form must reproduce the above # * copyright notice, this list of conditions and the following # * disclaimer in the documentation and/or other materials # * provided with the distribution. # * # * Neither the name of the copyright holder nor the names # * of any other contributors may be used to endorse or # * promote products derived from this software without # * specific prior written permission. # * # * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND # * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, # * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR # * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, # * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, # * OF SUCH DAMAGE. # ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------ # Smartly uncompress archives (zip only for now) # ------------------------------------------------------------------------------ utaz() { _ununzip() { unzip -o "$1" -d "$2" >/dev/null 2>&1 } _untar() { tar -xf "$1" -C "$2" } _ungzip() { tar -xzf "$1" -C "$2" } _unbzip2() { tar -xjf "$1" -C "$2" } _unxz() { tar -xJf "$1" -C "$2" } _unlzop() { lzop -d "$1" -o "$2/$(basename "${1%.*}")" } _unlzip() { if command -v plzip >/dev/null 2>&1; then plzip -d -c "$1" > "$2/$(basename "${1%.*}")" else lzip -d -c "$1" > "$2/$(basename "${1%.*}")" fi } for opt in $@; do case ${opt} in "-h" | "--help") echo "utaz: uncompress all the given files and/or the ones found in the given" echo " directories creating an host directory where needed." echo echo "Usage: utaz [option] [directorie(s)|file(s)]" echo echo "Options:" echo " -h, --help Display that help screen" echo " -d, --delete If decompression succeeded, delete the source file" echo " -c, --create-dir Always create a host directory" echo " -n, --no-dir Never create a host directory" echo return 0 ;; "-d" | "--delete") local willrm=1 ;; "-c" | "--create-dir") local createdir=1 ;; "-n" | "--no-dir") local nodir=1 ;; "-"*) disp E "Invalid option, use \"utaz --help\" to display options list" echo return 1 ;; *) # The ${opt%/} writing is to remove trailing / if any local LIST="${LIST} ${opt%/}" ;; esac done [[ -n ${createdir} && -n ${nodir} ]] && \ disp E "The --create-dir and --no-dir options are mutually exclusive." [[ -z ${LIST} ]] && local LIST="." for zitem in ${LIST}; do for f in "${zitem}"/*; do local dir="${f%.*}" local extractor="" case "$f" in *.zip) extractor="_ununzip" ;; *.tar.gz|*.tgz) extractor="_ungzip" ;; *.tar.bz2) extractor="_unbzip2" ;; *.tar.xz) extractor="_unxz" ;; *.tar.lz) extractor="_unlzop" ;; *.tar) extractor="_untar" ;; *) disp I "File ${f} is not a supported archive, skipping." continue ;; # Skip non-archive files esac # Verify binary existence local cmd=${extractor//_un/} command -v ${cmd} >/dev/null 2>&1 || { disp E "Binary ${cmd} necessary to extract ${f} is missing." continue } disp I "Processing archive ${f} with ${extractor}..." mkdir -p "${dir}" [[ $? -gt 0 ]] && disp E "The filesystem can't create directories, exit!" && return 1 ${extractor} "${f}" "${dir}" case $? in 0) [[ -n ${willrm} ]] && rm -f "${f}" && disp I "File ${zitem}/${f} deleted." ;; 1) disp W "Compression program returned a warning: deletion canceled." ;; *) disp E "The zip file seems corrupted, failed." rm -rf "${dir}" >/dev/null 2>&1 continue ;; esac if [[ -n ${createdir} ]]; then disp I "Archive extracted successfully in subdirectory." elif [[ -n ${nodir} ]]; then mv "./${dir}/"* ./ && rmdir "${dir}" disp I "Archive extracted successfully, no subdirectory needed." else # Set nullglob to ensure the array is empty if no files match shopt -s nullglob local contents=( "${dir}"/* ) # Check if exactly one item exists and if that item is a directory if [[ ${#contents[@]} -eq 1 ]] && [[ -d "${contents[0]}" ]]; then # Single directory detected mv "${contents[0]}"/* ./ && rmdir "${dir}" disp I "Archive extracted successfully, no subdirectory needed." else disp I "Archive extracted successfully in subdirectory." fi shopt -u nullglob fi done done } export -f utaz # ------------------------------------------------------------------------------ # Compress directories or files into one or more archive # ------------------------------------------------------------------------------ taz() { _doxz() { command -v xz >/dev/null 2>&1 || { disp E "The program 'xz' is not installed, aborting." return 127 } [[ $4 ]] && local verb='-v' # Display a warning for this format disp W "xz format is not suited for long term archiving." disp I "See https://www.nongnu.org/lzip/xz_inadequate.html for details." # Compresse to xz (lzma2) - Deprecated xz $verb --compress --keep -$3 -T $2 $1 return $? } _dolz() { local procopt="--threads $2" local command=plzip command -v plzip >/dev/null 2>&1 || { command -v lzip >/dev/null 2>&1 || { disp E "Program 'plzip' or 'lzip' are not installed, aborting." return 127 } command=lzip local procopt="" [[ $2 -gt 1 ]] && disp W "lzip doesn't support multithreading, falling back to 1 thread." && disp W "Consider installing plzip to obtain multithreading abilities." } [[ $4 ]] && local verb="-vv" # Compresse au format lzip (lzma) $command $verb $procopt --keep -$3 $1 return $? } _dogz() { local procopt="--processes $2" local command=pigz command -v pigz >/dev/null 2>&1 || { command -v gzip >/dev/null 2>&1 || { disp E "Programs 'pigz' or 'gzip' are not installed, aborting." return 127 } local command="gzip --compress" local procopt="" [[ $2 -gt 1 ]] && disp W "gzip doesn't support multithreading, falling back to 1 thread." && disp W "Consider installing pigz to obtain multithreading abilities." } [[ $4 ]] && local verb="--verbose" # Compresse au format bz2 $command $verb $procopt --keep -$3 $1 return $? } _dobz2() { local procopt="-p$2" local command=pbzip2 command -v pbzip2 >/dev/null 2>&1 || { command -v bzip2 >/dev/null 2>&1 || { disp E "The program 'pbzip2' or 'bzip2' are not installed, aborting." return 127 } local command=bzip2 local procopt="" [[ $2 -gt 1 ]] && disp W "bzip2 doesn't support multithreading, falling back to 1 thread." && disp W "Consider installing pbzip2 to obtain multithreading abilities." } [[ $4 ]] && local verb="-v" # Compresse au format bz2 $command $verb --compress $procopt --keep -$3 $1 return $? } _dolzo() { command -v lzop >/dev/null 2>&1 || { disp E "The program 'lzop' is not installed, aborting." return 127 } [[ $4 ]] && local verb='-v' [[ $2 -gt 1 ]] && disp W "lzop doesn't support multithreading, falling back to 1 thread." # Compresse au format lzo lzop --keep -$3 $1 return $? } for opt in $@; do case $opt in "-h" | "--help") echo "taz: archive all files of a directory." echo echo "Usage: taz [option] [--parallel=] [--format=] [directory1 ... directoryN]" echo echo "Options:" echo " -h, --help Display that help screen" echo " -d, --delete Delete source file or directory after success" echo " -f, --format Chose archive format in the given list. If several format are" echo " given, the smalest is kept" echo " -p, --parallel Number of threads to use (if allowed by underlying utility)" echo " -v, --verbose Display progress where possible" echo " -q, --quiet Display less messages (only errors and warnings)" echo " -1, .., -9 Compression level to use [1=fast/biggest, 9=slow/smallest]" echo echo "Supported archive format:" echo " Param.| programs | Algo. | Description" echo " ------+---------------+-------+----------------------------------------" echo " lz | plzip, lzip | lzma | Safe efficient default format" echo " xz | xz | lzma2 | Unsafe, not for long term" echo " bz2 | pbzip2, bzip2 | bzip2 | Historical but less efficient than lz" echo " gz | pigz, gzip | lz77 | Historical, safe, fast" echo " lzo | lzop | lzo | Very fast but no multithread" echo " tar | tar | tar | No compression" echo return 0 ;; "-d" | "--delete") local willrm=1 ;; "-f"?* | "--format"?*) local compform=$(echo "$opt" | cut -f 2- -d '=') ;; "-p"?* | "--parallel"?*) local nproc=$(echo "$opt" | cut -f 2- -d '=') ;; "-v" | "--verbose") local verbose=1 ;; "-q" | "--quiet") local quiet=1 ;; "-"*) local complevel=$(echo $opt | sed 's/-//') if ! [[ $complevel =~ ^[1-9]+$ ]]; then disp E "Invalid option, use taz --help to display options list" echo return 1 fi ;; *) local LIST="$LIST ${opt%/}" ;; esac done [[ ! $compform ]] && compform=lz # safe and efficient (unless data are already compressed) [[ ! $nproc ]] && nproc=1 [[ ! $complevel ]] && complevel=6 [[ $verbose -gt 1 && $quiet -gt 1 ]] && disp E "The --verbose and --quiet options can't be used together." for item in $LIST; do local donetar=0 disp I "Processing $item..." if [[ -d "$item" ]]; then disp I "\t Creating $item.tar... " tar -cf "$item.tar" "$item" if [[ ! $? -eq 0 ]]; then disp E "tar file creation failed, skipping to next item." continue fi local donetar=1 fi local fname=$item [[ $donetar -gt 0 ]] && fname=$item.tar # Skip compression part if tar is asked if [[ $compform != "tar" ]]; then disp I "\t Compressing archive..." _do$compform "$fname" "$nproc" "$complevel" "$verbose" [[ ! $? -eq 0 ]] && case $? in 127) disp E "Compression program unavailable, aborting." return 127 ;; *) disp E "Compression program returned an error, not deleting anything if asked, skipping to next item." continue ;; esac [[ $donetar -gt 0 ]] && rm "$fname" fi if [[ $willrm ]]; then disp I "\t Deleting original source as asked... " rm -r "$item" fi done unset quiet } export -f taz # ------------------------------------------------------------------------------ # EOF