Here is an update on terdon on strike's answer. This fixes some problems that it had with files that have version numbers in them, such as "VSCode-3.2.1.tar.gz" etc. As well as adds support for .tar.xz archives, and combines all the compressed tar options, since the -j and -z, etc, tar options are really only for archive creation, not extraction (it will auto detect). Also, the temp dir wasn't being removed after successful extraction in some cases, so I fixed that also.
#!/usr/bin/env bash
# https://superuser.com/questions/516873/commandline-program-to-extract-archives-with-automatic-subdirectry-detection
for file in "$@"
do
## Get the file's extension
ext=${file##*.}
## Special case for compressed tar files. They sometimes
## have extensions like tar.bz2 or tar.gz etc.
if [[ "$(basename "$file" ."$ext")" =~ \.tar$ ]]; then
if [[ "$ext" =~ ^(gz|bz2|xz|Z)$ ]]; then
ext="tar"
fi
fi
## Create the temp dir
tmpDir=$(mktemp -d XXXXXX);
case $ext in
7z)
7z -o "$tmpDir" e "$file"
;;
tar)
tar -xf "$file" -C "$tmpDir"
;;
rar)
unrar e "$file" "$tmpDir"
;;
zip)
unzip "$file" -d "$tmpDir"
;;
*)
echo "Unknown extension: '$ext', skipping..."
;;
esac
## Get the tmp dir's structure
tmpContents=( "$tmpDir"/* )
c=1
## If the tmpDir contains only one item and that is a directory
if [[ ${#tmpContents[@]} = 1 ]] && [[ -d "${tmpContents[0]}" ]]
then
## Move that directory to the current working directory,
## renaming it if a file/directory with the same name exists.
## Then delete the tmpDir.
dirName=${tmpContents[0]##*/}
[[ -e "$dirName" ]] && dirName="$dirName.$c"
while [[ -e "$dirName" ]]; do
((c++))
dirName="${dirName%.*}.$c" ## increment dirName.1 counter
done
mv "${tmpContents[0]}" "$dirName"
rm -r "$tmpDir"
else
## If the tmpDir contains anything more than a single directory,
## rename the tmpDir to the name of the archive, without extension.
## If a file/dir of that name already exists, add a counter.
dirName="${file##*/}" ## strip path
dirName="${dirName%.*}" ## strip extension
dirName="${dirName%.tar}" ## strip remaining tar extension
[[ -e "$dirName" ]] && dirName="$dirName.$c"
while [[ -e "$dirName" ]]; do
((c++))
dirName="${dirName%.*}.$c" ## increment dirName.1 counter
done
mv "$tmpDir" "$dirName"
fi
printf "Archive '%s' extracted to %s\n" "$file" "$dirName" >&2
done
This is apparently how aunpack
works anyway (minus the dirName incrementer we use), so if you don't want to install that, just add this to your $HOME/.local/bin/
or elsewhere, named unpack
/untar
, or something, chmod +x
.
Note, this solution depends on you also having installed 7zip
, unrar
, unzip
, and tar
, of course. (Or whichever of those filetypes you'll be using). If you won't be working with rar files, then unrar isn't necessary, etc.
Note 2: A terminal program called unar
apparently also exists that comes with some linux distros: "Unarchiver for a variety of file formats". It seems to behave in this way as well, but with a warning/option to rename/override/skip/quit, when it encounters a folder name that already exists during extraction. And it also seems to work with all of the above file types.
#!/bin/bash
rather than the more modern#!/usr/bin/env bash
?