Well, you could just extract into a subdirectory unconditionally and get rid of it afterwards if it ends up containing only a single item.
But why go for a sane and easy solution (courtesy of ilkkachu), when you can use awk
instead? :)
sunzip ()
{
if [ $# -ne 1 ] || ! [ -f "$1" ]
then
printf '%s\n' "Expected a filename as the first (and only) argument. Aborting."
return 1
fi
extract_dir="."
# Strip the leading and trailing information about the zip file (leaving
# only the lines with filenames), then check to make sure *all* filenames
# contain a /.
# If any file doesn't contain a / (i.e. is not located in a directory or is
# a directory itself), exit with a failure code to trigger creating a new
# directory for the extraction.
if ! unzip -l "$1" | tail -n +4 | head -n -2 | awk 'BEGIN {lastprefix = ""} {if (match($4, /[^/]+/)) {prefix=substr($4, RSTART, RLENGTH); if (lastprefix != "" && prefix != lastprefix) {exit 1}; lastprefix=prefix}}'
then
extract_dir="${1%.zip}"
fi
unzip -d "$extract_dir" "$1"
}
Quick'n'dirty. Works with InfoZIP's unzip
v6.0.
You might want to adapt it to your needs, e.g. to accept or automatically use additional parameters for unzip
, or to use a different name for the extraction subdirectory (which is currently determined from the name of the zip
file).
Oh, and I just noticed that this workaround correctly deals with the two most common situations (1. ZIP file contains a single directory with contents, 2. ZIP file contains lots of individual files and/or directories), but doesn't create a subdirectory when the ZIP file's root contains multiple directories but no files…
Edit: Fixed. The awk
script now stores the first component ("prefix") of each path contained in the ZIP file, and aborts as soon as it detects a prefix that differs from the previous one. This catches both multiple files and multiple directories (since both are bound to have different names), while ignoring ZIP files where everything is contained in the same subdirectory.