Skip to content

Instantly share code, notes, and snippets.

@artisin
Created January 7, 2023 07:42
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save artisin/6c19df8359d330b05a4d1e2dfb705646 to your computer and use it in GitHub Desktop.
Save artisin/6c19df8359d330b05a4d1e2dfb705646 to your computer and use it in GitHub Desktop.
unrarall
#!/usr/bin/env bash
#unrarall
# Copyright (C) 2011, 2012, 2013 Brendan Le Foll <brendan@fridu.net>
# Copyright (C) 2011, 2012, 2013 Dan Liew <dan@su-root.co.uk>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
##########################################################################
# Set some defaults
declare -x FIND="find" #set to gfind to use homebrew's GNU findutils on OSX Yosemite
declare -x SED="sed"
declare -x DIR=""
declare -x RM="rm"
declare -rx UNRARALL_VERSION="0.5.0"
declare -ix FORCE=0
declare -ix VERBOSE=0
declare -ix QUIET=0
declare -ix CKSFV=1
declare -ix ALLOW_FAILURES=0
declare -x UNRAR_METHOD="e"
declare -x CKSFV_FLAGS="-q -g"
declare -x UNRARALL_BIN="" #Leave empty to let unrarall to loop through UNRAR_BINARIES, setting this will disable searching through UNRAR_BINARIES
declare -x UNRAR_BINARIES=(unrar rar 7z) #Array of binaries to try and use, the order here is the order of precedence
declare -x UNRARALL_PID="$$"
declare -x UNRARALL_EXECUTABLE_NAME=$(basename $0;)
declare -x UNRARALL_PASSWORD_FILE="${HOME}/.unrar_passwords" #If this exists and unrar asks for a password
declare -ax UNRARALL_DETECTED_CLEAN_UP_HOOKS
declare -ax UNRARALL_CLEAN_UP_HOOKS_TO_RUN=(none)
declare -x UNRARALL_OUTPUT_DIR=""
declare -ix DEPTH=4
declare -ix SKIP_IF_EXISTS=0
declare -ix MV_BACKUP=0 # 0 -> we can use mv -b, 1 -> resort to shenanigans to not clobber when moving
`mv --help 2>&1 | grep "\-\-backup" 2>&1 >/dev/null`
MV_BACKUP=$?
function usage()
{
echo "Usage: ${UNRARALL_EXECUTABLE_NAME} [ --clean=<hook>[,<hook>] ] [ --force ] [ --full-path ]
[ --verbose | --quiet ] [--7zip | --backend=<backend> ] [--dry] [--disable-cksfv] [ --password-file <file> ]
[ --output DIRECTORY ] [ --find-binary <binary_path> ] [ --depth <amt> ] [--skip-if-exists] <DIRECTORY>
${UNRARALL_EXECUTABLE_NAME} --help
${UNRARALL_EXECUTABLE_NAME} --version
Usage (short options):
${UNRARALL_EXECUTABLE_NAME} [-f] [ -v | -q ] [-7] [-d] [-o DIRECTORY ] [-s] <DIRECTORY>
${UNRARALL_EXECUTABLE_NAME} -h
DESCRIPTON
${UNRARALL_EXECUTABLE_NAME} is a utility to unrar and clean up various files
(.e.g. rar files). Sub-directories are automatically recursed and if a rar file
exists in a sub-directory then the rar file is extracted into that subdirectory.
Use --clean= if you want to cleanup (delete files/folders). Otherwise no
cleaning is done. It can also be used to delete rar files that have already been
used for extraction with \"--clean=rar --force\". Use with caution!
OPTIONS
-7, --7zip Force using 7zip instead of trying to automatically find a
program to extract the rar files.
-d, --dry Dry run of unrar and cleaning. No action will actually be
performed.
--allow-failures If there were any successful extractions, ignore errors and
exit with code 0.
-s, --disable-cksfv Use cksfv (if present) to check the CRC of the downloaded
files (if present) before extracting
--clean= Clean if unrar extraction succeeds (use --force to override).
The clean up hooks specified determine what files/folders
are removed. By default this is 'none'. Hooks are separated
by a comma and are executed in order specified.
-f, --force Force unrar even if sfv check failed and if --clean will
clean even if unrar fails.
--full-path Extract full path inside rar files instead of just
extracting the files in the rar file which is the default
behaviour.
-h, --help Displays this help message and exits.
-v, --verbose Show extraction progress as ${UNRARALL_EXECUTABLE_NAME}
executes. This is not done by default
-q, --quiet Be completely quiet. No output will be written to the
screen
-o, --output Specify a directory to extract rar files. By default files
will be extracted in the directory containing the rar file.
--depth Maximum depth that nested rar files will be extracted. By
default, is a depth of 4.
--skip-if-exists Skip extraction of archive if all files already exists.
Note: files are only checked by name, not by contents!
--password-file Specify path to password file.
Default \"${UNRARALL_PASSWORD_FILE}\".
--backend Force particular backend (unrar, rar, or 7z)
--find-binary Force a particular find binary.
--version Give version information version.
"
#List the detected clean up hooks with their help information
detect_clean_up_hooks
echo "CLEAN UP HOOKS"
for hook in ${UNRARALL_DETECTED_CLEAN_UP_HOOKS[*]} ; do
echo "$hook : $( unrarall_clean_${hook} help)"
done
echo "all : Execute all the above hooks. They are executed in the order they are listed above."
echo "none : Do not execute any clean up hooks (default)."
echo "
VERSION: $UNRARALL_VERSION
"
}
function clean_up
{
#perform any necessary clean up
message error "${UNRARALL_EXECUTABLE_NAME} is exiting"
exit 1;
}
#Catch exit signals
trap clean_up SIGQUIT SIGINT SIGTERM
#function to display pretty messages
function message()
{
#Assume $1 is message type & $2 is message
#See http://www.frexx.de/xterm-256-notes/ for information on xterm colour codes
if [ "$QUIET" -ne 1 ]; then
case "$1" in
error)
#use escape sequence to show red text
echo -e "\033[31m${2}\033[0m" 1>&2
;;
ok)
#use escape sequence to show green text
echo -e "\033[32m${2}\033[0m"
;;
nnl)
#use echo -n to avoid new line
echo -n "$2"
;;
info)
echo "$2"
;;
*)
echo "$2"
esac
fi
}
#function to get flags for unrar binary
function getUnrarFlags()
{
#$1 is assumed to be unrar binary name
case "$1" in
unrar|rar)
echo -o+ -y
;;
7z)
echo -y
;;
echo)
echo
;;
*)
#This code should only be reached if the programmer has made an error
message error "Failed to determine flags for unsupported program \"$1\""
#We will probably be called in a sub shell but we need to kill the parent shell
kill -TERM ${UNRARALL_PID}
exit 1;
;;
esac
}
#function to check if an archive has been extracted already
function isAlreadyExtracted()
{
case "$1" in
unrar)
file_listing_cmd="unrar lb \"$2\""
;;
7z)
file_listing_cmd="7z L -ba -slt \"$2\" | grep Path | cut -d '=' -f 2"
;;
rar)
file_listing_cmd="rar lb \"$2\""
;;
echo)
return 1
;;
*)
message info "Unable to determine if \"$2\" was already extracted with \"$1\"."
message info "Assuming \"$2\" has never been extracted."
return 1
;;
esac
for rar_file in $(eval $file_listing_cmd); do
if [ ! -e $rar_file ]; then
return 1
fi
done
return 0
}
#function to check if file is password protected
function isRarEncrypted()
{
case "$1" in
unrar|rar)
rar_listing=$(unrar l -p- "$2")
echo "${rar_listing}" | grep -q -E "^\*"
if [ "$?" -eq 0 ] ; then
# RAR file contains encrypted files
echo 1
else
echo "${rar_listing}" | grep -q -E "^Details: .+ encrypted headers"
if [ "$?" -eq 0 ] ; then
# RAR file is encrypted (even the file listing)
echo 1
else
# RAR file is not encrypted
echo 0
fi
fi
;;
7z)
rar_listing=$(7z l -slt -p- "$2")
echo "${rar_listing}" | grep -q -E "^Encrypted = \+$"
if [ "$?" -eq 0 ] ; then
# RAR file contains encrypted files
echo 1
else
echo "${rar_listing}" | grep -q -E "^Error: .+: Can not open encrypted archive\. Wrong password\?$"
if [ "$?" -eq 0 ] ; then
# RAR file is encrypted (even the file listing)
echo 1
else
# RAR file is not encrypted
echo 0
fi
fi
;;
echo)
echo 0
;;
*)
#This code should only be reached if the programmer has made an error
message error "Failed to check encryption using unsupported program \"$1\""
#We will probably be called in a sub shell but we need to kill the parent shell
kill -TERM ${UNRARALL_PID}
exit 1;
;;
esac
}
function detect_clean_up_hooks()
{
[ $VERBOSE -eq 1 ] && message info "Detecting clean up hooks..."
HOOKS=$(declare -F | grep -Eo ' unrarall_clean_[_a-z]+')
index=0
EMPTY_FOLDER=0
for hook in $HOOKS ;do
[ $VERBOSE -eq 1 ] && message info "Found $hook";
if [ "$hook" == "unrarall_clean_empty_folders" ]; then
# it is executed last for --clean=all
EMPTY_FOLDER=1
continue
fi
UNRARALL_DETECTED_CLEAN_UP_HOOKS[$index]=$( echo "$hook" | sed_wrapper 's/unrarall_clean_//')
((index++))
done
if [ ${EMPTY_FOLDER} -eq 1 ]; then
UNRARALL_DETECTED_CLEAN_UP_HOOKS[$index]="empty_folders"
fi
}
#Prints out an escaped version of 1st argument for use in find's regex
#This is posix egrep style regex
function find_regex_escape()
{
echo "$1" | sed_wrapper 's#\\#\\\\#g ;s/\?/\\?/g ; s/\./\\./g ; s/\+/\\+/g ; s/\*/\\*/g; s/\^/\\^/g ; s/\$/\\$/g; s/\[/\\[/g; s/\]/\\]/g; s/\(/\\(/g; s/\)/\\)/g; s/\|/\\|/g;'
if [ $? -ne 0 ]; then
message error "Failed to escape"
exit 1
fi
}
#Helper function for hooks to remove a single file/folder
# unrarall-remove-file <FILE> <HOOKNAME>
function unrarall_remove_file_or_folder()
{
if [ -f "./${1}" -o -d "./${1}" ]; then
[ "$VERBOSE" -eq 1 ] && message nnl "Hook ${2}: Found ${1} . Attempting to remove..."
${RM} -rf $( [ "$VERBOSE" -eq 1 ] && echo '--verbose') "./${1}"
[ $? -ne 0 ] && message error "Could not remove ${1}" || message ok "Success"
else
[ "$VERBOSE" -eq 1 ] && message info "Hook ${2}: No ${1} file/folder found."
fi
}
#Start of clean-up hooks
# unrarall_clean_* <MODE> <SFILENAME> <RARFILE_DIR>
# <MODE> is either help or clean
# <SFILENAME> is the name of the rar file but with rar suffix removed
# <RARFILE_DIR> is the directory containing the rar files
# Hooks should use ${RM} instead of the 'rm' command
# Hooks cannot be named "all" or "none" as these are reserved.
# Hooks can assume that the current working directory is the directory where files
# extracted. This is not necessarily the same as <RARFILE_DIR> is --output was specified
function unrarall_clean_nfo()
{
case "$1" in
help)
echo "Removes .nfo files with the same name as the .rar file"
;;
clean)
[ $VERBOSE -eq 1 ] && message info "Deleting ${2}.nfo"
${RM} -f "${2}.nfo"
;;
*)
message error "Could not execute clean-nfo hook"
;;
esac
}
function unrarall_clean_rar()
{
case "$1" in
help)
echo "Removes rar files and sfv files"
;;
clean)
# Strip out trailing slash if there is one. If we leave it there it'll break the regex passed to find
_RARFILE_DIR="${3%/}"
[ $VERBOSE -eq 1 ] && message info "Deleting ${2} rar files (in \"${_RARFILE_DIR}\")..."
if [ ! -d "${_RARFILE_DIR}" ]; then
message error "RARFILE_DIR (${_RARFILE_DIR}) is not a directory"
return
fi
#-maxdepth 1 is very important, we only want to delete rar files in the current directory!
find_wrapper "${_RARFILE_DIR}" -maxdepth 1 -type f -iregex "${_RARFILE_DIR}/$(find_regex_escape "$2")"'\.(sfv|[0-9]+|[r-z][0-9]+|rar|part[0-9]+\.rar)$' -exec ${RM} -f $( [ $VERBOSE -eq 1 ] && echo '-v') '{}' \;
;;
*)
message error "Could not execute clean-rar hook"
;;
esac
}
function unrarall_clean_osx_junk()
{
case "$1" in
help)
echo "Removes junk OSX files"
;;
clean)
unrarall_remove_file_or_folder ".DS_Store" "osx_junk"
;;
*)
message error "Could not execute osx_junk hook"
;;
esac
}
function unrarall_clean_windows_junk()
{
case "$1" in
help)
echo "Removes junk Windows files"
;;
clean)
unrarall_remove_file_or_folder "Thumbs.db" "windows_junk"
;;
*)
message error "Could not execute windows_junk hook"
;;
esac
}
function unrarall_clean_covers_folders()
{
case "$1" in
help)
echo "Removes junk Covers folders"
;;
clean)
[ "$VERBOSE" -eq 1 ] && message info "Removing all Covers/ folders"
find_wrapper . -depth -type d -iname 'covers' -exec ${RM} -rf $( [ $VERBOSE -eq 1 ] && echo '-v') '{}' \;
;;
*)
message error "Could not execute covers_folders hook"
;;
esac
}
function unrarall_clean_proof_folders()
{
case "$1" in
help)
echo "Removes junk Proof folders"
;;
clean)
[ "$VERBOSE" -eq 1 ] && message info "Removing all Proof/ folders"
find_wrapper . -depth -type d -iname 'proof' -exec ${RM} -rf $( [ $VERBOSE -eq 1 ] && echo '-v') '{}' \;
;;
*)
message error "Could not execute proof_folders hook"
;;
esac
}
function unrarall_clean_sample_folders()
{
case "$1" in
help)
echo "Removes Sample folders"
;;
clean)
[ "$VERBOSE" -eq 1 ] && message info "Removing all Sample/ folders"
find_wrapper . -type d -iname 'sample' -exec ${RM} -rf $( [ $VERBOSE -eq 1 ] && echo '-v') '{}' \;
;;
*)
message error "Could not execute sample_folders hook"
;;
esac
}
function unrarall_clean_sample_videos()
{
case "$1" in
help)
echo "Removes video files with \"sample\" as a prefix and a similar name to the rar file (without extension)"
;;
clean)
[ "$VERBOSE" -eq 1 ] && message info "Removing video files with \"sample\" prefix"
find_wrapper . -type f -iregex '^\./sample.*'"$(find_regex_escape "$2")"'\.(asf|avi|mkv|mp4|m4v|mov|mpg|mpeg|ogg|webm|wmv)$' -exec ${RM} -f $( [ $VERBOSE -eq 1 ] && echo '-v') '{}' \;
;;
*)
message error "Could not execute sample_videos hook"
;;
esac
}
function unrarall_clean_empty_folders()
{
case "$1" in
help)
echo "Removes empty folders in the folder containing the found rar file(s)"
;;
clean)
[ "$VERBOSE" -eq 1 ] && message info "Removing empty folders"
if [ ! -d "${3}" ]; then
message error "RARFILE_DIR (${3}) is not a directory"
return
fi
find_wrapper "${3}" -depth -mindepth 1 -type d -empty -exec rmdir $( [ $VERBOSE -eq 1 ] && echo '-v') '{}' \;
;;
*)
message error "Could not execute empty_folders hook"
;;
esac
}
#end of clean-up hooks
function safe_move()
{
if [ "$MV_BACKUP" -eq 0 ]; then
mv -b "${1}" "${2}"
else
mv -n "${1}" "${2}"
FILE_COUNT=1
while [ -e "${1}" ]; do
mv -n "${1}" "${2}.${FILE_COUNT}"
let FILE_COUNT=FILE_COUNT+1
done
fi
}
# Detect the version of sed available and create a wrapper function
# that will behave like sed using "extended regular expressions"
# This is to force consistency across multiple platforms.
if [ "X$(${SED} --version >/dev/null 2>&1 ; echo $?)" == "X0" ]; then
# GNU find supports `--version`
[ $VERBOSE -eq 1 ] && message info "Detected GNU sed"
function sed_wrapper() {
${SED} --regexp-extended "$@"
}
else
# Assume *BSD/macOS sed
[ $VERBOSE -eq 1 ] && message info "Detected BSD sed"
function sed_wrapper() {
${SED} -E "$@"
}
fi
# Parse command line arguments
if [ "$#" -lt 1 ]; then
message error "Insufficient number of arguments. See ${UNRARALL_EXECUTABLE_NAME} --help"
exit 1;
fi
# Save a copy of the original cmd + args
SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
SCRIPT_NAME=`basename "${BASH_SOURCE[0]}"`
UNRARALL_CMD="$SCRIPT_DIR/$SCRIPT_NAME"
while [ -n "$1" ]; do
if [ $# -gt 1 ] || [ $( echo "$1" | grep -Ec '^(-h|--help|--version)$' ) -eq 1 ] ; then
# save all the args passed to the cmd, except for --depth
if [ "${1}" != "--depth" ]; then
UNRARALL_CMD="${UNRARALL_CMD} ${1}"
fi
#Handle optional arguments
case "$1" in
-h | --help )
usage
exit 0
;;
--version )
echo "$UNRARALL_VERSION"
exit 0
;;
-d | --dry )
UNRARALL_BIN=echo
RM="echo"
;;
--allow-failures )
ALLOW_FAILURES=1
;;
-s | --disable-cksfv )
CKSFV=0
;;
--clean=*)
index=0;
for hook in $( echo "$1" | sed_wrapper 's/--clean=//' | tr , " ") ; do
UNRARALL_CLEAN_UP_HOOKS_TO_RUN[$index]="$hook"
((index++))
done
[ $index -eq 0 ] && { message error "Clean up hooks must be specified when using --clean="; exit 1; }
;;
-f | --force )
FORCE=1
;;
-v | --verbose )
VERBOSE=1
CKSFV_FLAGS="-g"
;;
-q | --quiet )
VERBOSE=0
QUIET=1
;;
--full-path )
UNRAR_METHOD="x"
;;
-7 | --7zip)
# to ensure that if --dry and then --7zip are used then we still do a dry run
if [ "$UNRARALL_BIN" != 'echo' ]; then
UNRARALL_BIN=7z
fi
;;
-o | --output)
shift
UNRARALL_CMD="${UNRARALL_CMD} ${1}"
UNRARALL_OUTPUT_DIR="$1"
if [ ! -d "${UNRARALL_OUTPUT_DIR}" ]; then
message error "\"${UNRARALL_OUTPUT_DIR}\" is not a directory"
exit 1
else
message info "Using \"${UNRARALL_OUTPUT_DIR}\" as output directory"
fi
# Make the directory absolute. This is necessary
# because we may be changing directory multiple times
UNRARALL_OUTPUT_DIR="$(cd "${UNRARALL_OUTPUT_DIR}" ; pwd)"
;;
--depth)
shift
DEPTH="$1"
case $DEPTH in
''|*[!0-9]*)
message error "\"${DEPTH}\" is not a number"
exit 1
;;
*)
message info "Using \"${DEPTH}\" as as the maximum depth"
;;
esac
;;
--skip-if-exists)
SKIP_IF_EXISTS=1
;;
--password-file)
shift
UNRARALL_CMD="${UNRARALL_CMD} ${1}"
if [ ! -e "$1" ]; then
message error "\"$1\" does not exist."
exit 1
fi
if [ ! -f "$1" ] && [ ! -L "$1" ]; then
message error "\"$1\" is not a file/symlink."
exit 1
fi
password_file_dir="$(cd $(dirname $1) && pwd)"
password_file_name="$(basename $1)"
UNRARALL_PASSWORD_FILE="${password_file_dir}/${password_file_name}"
message info "Using \"${UNRARALL_PASSWORD_FILE}\" as password file"
;;
--backend=*)
# If using `--dry` don't modify UNRARALL_BIN
requested_backend="$( echo "$1" | sed_wrapper 's/--backend=//')"
if [ "$UNRARALL_BIN" != 'echo' ]; then
case "${requested_backend}" in
unrar)
UNRARALL_BIN=unrar
;;
7z)
UNRARALL_BIN=7z
;;
rar)
UNRARALL_BIN=rar
;;
*)
message error "Unsupported backend \"${requested_backend}\""
exit 1
;;
esac
fi
;;
--find-binary)
shift
UNRARALL_CMD="${UNRARALL_CMD} ${1}"
FIND="${1}"
;;
*)
# user issued unrecognised option
message error "Unrecognised option: $1"
usage
exit 1
;;
esac
else
#Handle mandatory argument
DIR="$1"
fi
shift
done
# Detect the version of find available and create a wrapper function that
# that will behave like find using posix egrep style regular expressions.
# This is to force consistency across multiple platforms.
# Check FIND binary exists
if ! type -P "${FIND}" > /dev/null 2>&1 ; then
message error "Cannot find the \"find\" binary (\"${FIND}\") in your path"
exit 1
fi
# FIXME: Require -iregex support for now. We should try to teach the wrappers
# how to fall back to `-regex` or something...
if [ "$(${FIND} /dev/null -iregex 'test' > /dev/null 2>&1 ; echo $?)" -ne "0" ]; then
message error "Your version of find (\"${FIND}\") does not support the \"-iregex\" flag"
exit 1
fi
if [ "X$(${FIND} --version >/dev/null 2>&1 ; echo $?)" == "X0" ]; then
# GNU find supports `--version`
[ $VERBOSE -eq 1 ] && message info "Detected GNU find"
function find_wrapper() {
if [ $# -lt 2 ]; then
message error "Invalid args to find_wrapper"
exit 1
fi
args=("$@")
unset args[0]
${FIND} "$1" -regextype posix-egrep "${args[@]}"
}
else
# Assume *BSD/macOS find
[ $VERBOSE -eq 1 ] && message info "Detected BSD find"
function find_wrapper() {
${FIND} -E "$@"
}
fi
[ $VERBOSE -eq 1 ] && message info "Using find: ${FIND}"
detect_clean_up_hooks
#Verify selected clean-up hooks are ok
if [ ${#UNRARALL_CLEAN_UP_HOOKS_TO_RUN[*]} -eq 1 ] && [ $( echo "${UNRARALL_CLEAN_UP_HOOKS_TO_RUN[0]}" | grep -Ec '^(all|none)$' ) -eq 1 ] ; then
#Don't need to do anything
[ $VERBOSE -eq 1 ] && message info "Using virtual clean-up hook ${UNRARALL_CLEAN_UP_HOOKS_TO_RUN[0]}"
else
#Loop through clean up hooks and check it's allowed
for hook_to_use in ${UNRARALL_CLEAN_UP_HOOKS_TO_RUN[*]} ; do
HOOK_FOUND=0
for hook in ${UNRARALL_DETECTED_CLEAN_UP_HOOKS[*]}; do
if [ "$hook_to_use" = "$hook" ]; then
HOOK_FOUND=1
break;
fi
done
if [ $HOOK_FOUND -eq 0 ]; then
message error "Hook $hook_to_use is not a valid clean up hook. See --help"
exit 1;
fi
done
fi
# Current Dir check
if [ "${DIR}" == "." ]; then
DIR="`pwd`"
fi
#If No user specified binary, cycle through array and try and find a binary that can be used
if [ -z "${UNRARALL_BIN}" ]; then
for (( index=0; index < ${#UNRAR_BINARIES[@]}; index++)); do
#Check for binary
[ $VERBOSE -eq 1 ] && message nnl "Looking for ${UNRAR_BINARIES[$index]}..."
if type -P ${UNRAR_BINARIES[$index]} 2>&1 > /dev/null ; then
#Binary found
UNRARALL_BIN="${UNRAR_BINARIES[$index]}"
[ $VERBOSE -eq 1 ] && message ok "found"
break;
else
[ $VERBOSE -eq 1 ] && message error "not found"
fi
#check if end of list
if [ $index -eq $(( ${#UNRAR_BINARIES[@]} -1)) ]; then
message error "Failed to find binary to perform rar extraction. The following binaries were looked for ${UNRAR_BINARIES[*]}"
exit 1;
fi
done
else
#check the manually specified binary exists
if ! type -P ${UNRARALL_BIN} 2>&1 > /dev/null ; then
message error "The manually specified binary ${UNRARALL_BIN} cannot be found"
exit 1;
fi
fi
#Inform the user about the binary chosen
[ $VERBOSE -eq 1 ] && message info "Using \"${UNRARALL_BIN}\" to extract rar files" ;
# Check $DIR exists and is a directory
if [ -d "$DIR" ]; then
message normal "Working over directory \"${DIR}\""
else
message error "Cannot find directory \"${DIR}\""
exit 1;
fi
CURRENT_DIR=`pwd`
#find all files
COUNT=0
FAIL_COUNT=0
#modify IFS for new lines so filenames with spaces do not get split
IFS_TEMP=$IFS
IFS=$(echo -en "\n\b")
MATCHRARMIME='x-rar'
if [ "$CKSFV" -eq 1 ]; then
if ! type -P cksfv 2>&1 > /dev/null ; then
message info "Install cksfv in order to get CRC checked before using unrar"
CKSFV=0
fi
fi
# assuming only the .rar files are of interest
for file in $(find_wrapper "$DIR" -depth -iregex '.*\.(rar|001)$'); do
filename=`basename "${file}"`
# Strip extension off filename
sfilename="${filename%.*}"
# if rar is of style partxx.rar then only extract $filename.part01.rar
if [[ "$file" =~ .part[0-9]+.rar$ ]]; then
if [[ "$file" =~ .part0*1.rar$ ]]; then
sfilename=`echo "$sfilename" | sed_wrapper 's/\.part[0-9]+$//'`
[ $VERBOSE -eq 1 ] && message info "Using rar 3.x split archive format"
else
continue
fi
# allow rar archives with .001 -> .999 naming
elif [[ "$file" =~ .[0-9][0-9][0-9]$ ]]; then
if [[ "$file" =~ .001$ ]]; then
message info "Your packager is wrong, 001 is not an acceptable filename for a rar file. We'll attempt to clean up his mess anyways..."
else
continue
fi
fi
# check file really is a rar file
if [[ ! "`file --mime "${file}"`" =~ $MATCHRARMIME ]]; then
message error "Skipping file \"${file}\" because it does not appear to be a valid rar file."
# Treat this as a failure so we exit with a non zero exit code
let FAIL_COUNT=FAIL_COUNT+1
continue
fi
let COUNT=COUNT+1
# move to directory
dirname=`dirname "$file"`
cd "$dirname"
RARFILE_DIR="$(pwd)"
SUCCESS=0
if [ "$CKSFV" -eq 1 ]; then
# check an sfv file is present
if [ -e "${sfilename}.sfv" ]; then
message info "Running cksfv using ${sfilename}.sfv"
eval cksfv ${CKSFV_FLAGS} \"${sfilename}.sfv\"
# CKSFV will print error message even with -q on error
SUCCESS=$?
fi
fi
# Compute fullpath to rar file
filenameAbsolute="$(pwd)/${filename}"
# Check if file is extracted
if [ "$SKIP_IF_EXISTS" -eq 1 ] && [ "$SUCCESS" -eq 0 ] && [ "$FORCE" -eq 0 ]; then
isAlreadyExtracted ${UNRARALL_BIN} ${filenameAbsolute}
if [ "$?" -eq 0 ]; then
message ok "File \"${filenameAbsolute}\" appears to have already been extracted, skipping..."
continue
fi
fi
# Switch to a temporary directory so the extracted file does not clobber anything
# some special handling here to support macos: http://unix.stackexchange.com/a/84980/210181
tmp_dir=`mktemp -d -p . 2>/dev/null || TMPDIR=$(pwd) mktemp -d -t 'unrarall'`
if [ $? -ne 0 ] || [ -z ${tmp_dir} ] || [ ! -d "${tmp_dir}" ]; then
message error "Failed to create a temporary directory to handle extraction"
exit 1
fi
cd "${tmp_dir}"
if [ $? -ne 0 ]; then
message error "Failed to switch to tmp directory \"${tmp_dir}\""
exit 1
fi
tmp_dir=`pwd`
# unrar file if SFV checked out or --force was given
if [ "$SUCCESS" -eq 0 ] || [ "$FORCE" -eq 1 ]; then
message nnl "Extracting (${UNRAR_METHOD}) \"$file\"..."
file_encrypted=$(isRarEncrypted ${UNRARALL_BIN} ${filenameAbsolute})
if [ "${file_encrypted}" -eq 0 ]; then
# The file is not encrypted. We're extracting without a password
# Use eval so that redirection gets evaluated correctly
eval ${UNRARALL_BIN} ${UNRAR_METHOD} $( getUnrarFlags ${UNRARALL_BIN}) -p- "\"${filenameAbsolute}\"" \
$([ "$VERBOSE" -eq 0 -a "X${UNRARALL_BIN}" != "Xecho" ] && echo ">/dev/null")
SUCCESS=$?
else
# The file is encrypted. if we have a password file, then use it
if [ -s "${UNRARALL_PASSWORD_FILE}" ] ; then
[ $VERBOSE -eq 1 ] && message info "This archive is encrypted. Trying passwords from password file \"${UNRARALL_PASSWORD_FILE}\"..."
while true; do read password || break
# Use eval so that redirection gets evaluated correctly
eval ${UNRARALL_BIN} ${UNRAR_METHOD} $( getUnrarFlags ${UNRARALL_BIN}) -p"\"$password\"" "\"${filenameAbsolute}\"" \
$([ "$VERBOSE" -eq 0 -a "X${UNRARALL_BIN}" != "Xecho" ] && echo ">/dev/null")
SUCCESS=$?
if [ "$SUCCESS" -eq 0 ] ; then
[ $VERBOSE -eq 1 ] && message ok "Extraction succeeded using password: \"${password}\""
break
else
[ $VERBOSE -eq 1 ] && message error "Extraction failed using password: \"${password}\""
fi
done < "$UNRARALL_PASSWORD_FILE"
else
message error "Archive \"$file\" is encrypted. Please put the password in \"${UNRARALL_PASSWORD_FILE}\" to extract it."
SUCCESS=1
fi
fi
fi
# Check if any recursive extraction is needed
if [ "$SUCCESS" -eq 0 ] && [ "${DEPTH}" -gt 0 ] && [[ -n `find_wrapper . -depth -iregex '.*\.(rar|001)$'` ]]; then
[ $VERBOSE -eq 1 ] && message ok "Detected rar archives inside of \"$file\", recursively extracting"
let NEW_DEPTH=DEPTH-1
eval $UNRARALL_CMD --depth $NEW_DEPTH ${tmp_dir}
if [ $? -ne 0 ]; then
message error "Failed to recursively extract \"${filenameAbsolute}\""
SUCCESS=1
fi
fi
# if fail remove from count
if [ "$SUCCESS" -eq 0 ] && [ "$FORCE" -eq 0 ] ; then
message ok "ok";
elif [ "$SUCCESS" -eq 0 ] && [ "$FORCE" -eq 1 ] ; then
message ok "ok (forced)";
else
let COUNT=COUNT-1
let FAIL_COUNT=FAIL_COUNT+1
[ "$FORCE" -eq 0 ] && message error "failed" || message error "failed (forced)"
fi
# Save the files extracted
files=( $(find_wrapper . -mindepth 1 -type f) )
# Save the empty directories extracted
empty_directories=( $(find_wrapper . -mindepth 1 -type d -empty) )
# If user specified extraction directory change to it now
if [ -n "${UNRARALL_OUTPUT_DIR}" ]; then
cd "${UNRARALL_OUTPUT_DIR}"
if [ $? -ne 0 ]; then
message error "Failed to switch to output directory \"${UNRARALL_OUTPUT_DIR}\""
exit 1
fi
else
cd "${RARFILE_DIR}"
if [ $? -ne 0 ]; then
message error "Failed to switch to rar directory \"${RARFILE_DIR}\""
exit 1
fi
fi
# Move extracted files to cwd safely
for extracted_file in "${files[@]}"; do
mkdir -p "$(dirname ${extracted_file})"
if [ $? -ne 0 ]; then
message error "Unable to create directory structure for \"$(dirname ${extracted_file})\""
exit 1
fi
safe_move "${tmp_dir}/${extracted_file}" "${extracted_file}"
if [ $? -ne 0 ]; then
message error "Failed to move \"${extracted_file}\" to \"${RARFILE_DIR}\""
exit 1
fi
done
for dir in "${empty_directories[@]}"; do
mkdir -p "${dir}"
if [ $? -ne 0 ]; then
message error "Unable to create directory structure for \"${dir}\""
exit 1
fi
done
# Remove the tmp directory
if [ -d "${tmp_dir}" ]; then
find_wrapper "${tmp_dir}" -mindepth 1 -type d -empty -delete
rmdir "${tmp_dir}"
if [ $? -ne 0 ]; then
message error "Failed cleanup temporary directory \"${tmp_dir}\""
exit 1
fi
fi
#Perform clean up if necessary
if [ ${UNRARALL_CLEAN_UP_HOOKS_TO_RUN[0]} != "none" ]; then
if [ "$SUCCESS" -eq 0 ] || [ "$FORCE" -eq 1 ] ; then
message nnl "Running hooks..."
if [ ${UNRARALL_CLEAN_UP_HOOKS_TO_RUN[0]} = "all" ]; then
#Run every clean up hook
for hook in ${UNRARALL_DETECTED_CLEAN_UP_HOOKS[*]} ; do
message nnl "$hook "
( unrarall_clean_$hook clean $sfilename "${RARFILE_DIR}" )
done
else
#Run selected clean up hooks
for hook in ${UNRARALL_CLEAN_UP_HOOKS_TO_RUN[*]} ; do
message nnl "$hook "
( unrarall_clean_$hook clean $sfilename "${RARFILE_DIR}")
done
fi
message ok "Finished running hooks"
else
message error "Couldn't do requested clean because ${UNRARALL_BIN} extracted unsuccessfully. Use --force to override this behaviour"
fi
fi
cd "$CURRENT_DIR"
done
IFS=$IFS_TEMP
if [ "$QUIET" -ne 1 ]; then
if [ "$COUNT" -ne 0 ]; then
EXIT_PHRASE="found and extracted"
if [ ${UNRARALL_CLEAN_UP_HOOKS_TO_RUN[0]} != "none" ]; then
EXIT_PHRASE="found, extracted and then cleaned using the following hooks: ${UNRARALL_CLEAN_UP_HOOKS_TO_RUN[*]}"
fi
message info "$COUNT rar files $EXIT_PHRASE"
else
message error "no rar files extracted"
fi
fi
if [ "$FAIL_COUNT" -ne 0 ]; then
if [ "$QUIET" -ne 1 ]; then
message error "${FAIL_COUNT} failure(s)"
fi
if [ "$ALLOW_FAILURES" -eq 0 ]; then
exit 1
else
if [ "$COUNT" -eq 0 ]; then
exit 1
else
message info "${COUNT} success(es)"
fi
fi
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment