ohmyzsh/plugins/extract/extract.plugin.zsh
Marc Cornellà d47e1d65f6
fix(extract): safely remove extract directory
The previous code would remove the extract directory if the command failed.
This could be bad because we're not checking if the extract directory
already existed (since we're using `mkdir -p`), so it could be possible
that the extract operation failed, and we'd be removing a directory that
already existed and had files in it.

This change only removes the directory if there are no files in it, regardless
of whether the extract operation was successful or not. This is much safer.
2023-04-02 16:33:54 +02:00

108 lines
3.5 KiB
Bash

alias x=extract
extract() {
setopt localoptions noautopushd
if (( $# == 0 )); then
cat >&2 <<'EOF'
Usage: extract [-option] [file ...]
Options:
-r, --remove Remove archive after unpacking.
EOF
fi
local remove_archive=1
if [[ "$1" == "-r" ]] || [[ "$1" == "--remove" ]]; then
remove_archive=0
shift
fi
local pwd="$PWD"
while (( $# > 0 )); do
if [[ ! -f "$1" ]]; then
echo "extract: '$1' is not a valid file" >&2
shift
continue
fi
local success=0
local extract_dir="${1:t:r}"
local file="$1" full_path="${1:A}"
# Create an extraction directory based on the file name
command mkdir -p "$extract_dir"
builtin cd -q "$extract_dir"
case "${file:l}" in
(*.tar.gz|*.tgz)
(( $+commands[pigz] )) && { tar -I pigz -xvf "$full_path" } || tar zxvf "$full_path" ;;
(*.tar.bz2|*.tbz|*.tbz2)
(( $+commands[pbzip2] )) && { tar -I pbzip2 -xvf "$full_path" } || tar xvjf "$full_path" ;;
(*.tar.xz|*.txz)
(( $+commands[pixz] )) && { tar -I pixz -xvf "$full_path" } || {
tar --xz --help &> /dev/null \
&& tar --xz -xvf "$full_path" \
|| xzcat "$full_path" | tar xvf - } ;;
(*.tar.zma|*.tlz)
tar --lzma --help &> /dev/null \
&& tar --lzma -xvf "$full_path" \
|| lzcat "$full_path" | tar xvf - ;;
(*.tar.zst|*.tzst)
tar --zstd --help &> /dev/null \
&& tar --zstd -xvf "$full_path" \
|| zstdcat "$full_path" | tar xvf - ;;
(*.tar) tar xvf "$full_path" ;;
(*.tar.lz) (( $+commands[lzip] )) && tar xvf "$full_path" ;;
(*.tar.lz4) lz4 -c -d "$full_path" | tar xvf - ;;
(*.tar.lrz) (( $+commands[lrzuntar] )) && lrzuntar "$full_path" ;;
(*.gz) (( $+commands[pigz] )) && pigz -dk "$full_path" || gunzip -k "$full_path" ;;
(*.bz2) bunzip2 "$full_path" ;;
(*.xz) unxz "$full_path" ;;
(*.lrz) (( $+commands[lrunzip] )) && lrunzip "$full_path" ;;
(*.lz4) lz4 -d "$full_path" ;;
(*.lzma) unlzma "$full_path" ;;
(*.z) uncompress "$full_path" ;;
(*.zip|*.war|*.jar|*.ear|*.sublime-package|*.ipa|*.ipsw|*.xpi|*.apk|*.aar|*.whl) unzip "$full_path" ;;
(*.rar) unrar x -ad "$full_path" ;;
(*.rpm)
rpm2cpio "$full_path" | cpio --quiet -id ;;
(*.7z) 7za x "$full_path" ;;
(*.deb)
command mkdir -p "control" "data"
ar vx "$full_path" > /dev/null
builtin cd -q control; extract ../control.tar.*
builtin cd -q ../data; extract ../data.tar.*
builtin cd -q ..; command rm *.tar.* debian-binary ;;
(*.zst) unzstd "$full_path" ;;
(*.cab) cabextract "$full_path" ;;
(*.cpio|*.obscpio) cpio -idmvF "$full_path" ;;
(*.zpaq) zpaq x "$full_path" ;;
(*)
echo "extract: '$file' cannot be extracted" >&2
success=1 ;;
esac
(( success = success > 0 ? success : $? ))
(( success == 0 && remove_archive == 0 )) && command rm "$full_path"
shift
# Go back to original working directory
builtin cd -q "$pwd"
# If content of extract dir is a single directory, move its contents up
# Glob flags:
# - D: include files starting with .
# - N: no error if directory is empty
# - Y2: at most give 2 files
local -a content
content=("${extract_dir}"/*(DNY2))
if [[ ${#content} -eq 1 && -d "${content[1]}" ]]; then
command mv -f "${content[1]}" .
command rmdir "$extract_dir"
elif [[ ${#content} -eq 0 ]]; then
command rmdir "$extract_dir"
fi
done
}