How can I tell if "unzip" will create a single folder ahead of time?

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.


From the manual...

[-d exdir]

An optional directory to which to extract files. By default, all files and subdirectories are recreated in the current directory; the -d option allows extraction in an arbitrary directory (always assuming one has permission to write to the directory). This option need not appear at the end of the command line; it is also accepted before the zipfile specification (with the normal options), immediately after the zipfile specification, or between the file(s) and the -x option. The option and directory may be concatenated without any white space between them, but note that this may cause normal shell behavior to be suppressed. In particular, -d ~ (tilde) is expanded by Unix C shells into the name of the user's home directory, but -d~ is treated as a literal subdirectory ~ of the current directory.

So...

unzip -d new_dir zipfile.zip

This creates a directory, new_dir, and extracts the archive within it, which avoids the potential mess every time even without looking first. It is also very useful to look at man unzip. More help for manual pages.


You can use my script using mv.

mkdir downloaded

mv downloaded.zip downloaded && cd downloaded

unzip downloaded.zip && cd

You'll create a folder (downloaded) and you'll unzip it in the folder you created.

Tags:

Zip