77
votes

The unzip command doesn't have an option for recursively unzipping archives.

If I have the following directory structure and archives:

/Mother/Loving.zip
/Scurvy/Sea Dogs.zip
/Scurvy/Cures/Limes.zip

And I want to unzip all of the archives into directories with the same name as each archive:

/Mother/Loving/1.txt
/Mother/Loving.zip
/Scurvy/Sea Dogs/2.txt
/Scurvy/Sea Dogs.zip
/Scurvy/Cures/Limes/3.txt
/Scurvy/Cures/Limes.zip

What command or commands would I issue?

It's important that this doesn't choke on filenames that have spaces in them.

11
How about long file paths... beyond 260 characters...: superuser.com/a/1263234/439537 - Andrew

11 Answers

135
votes

If you want to extract the files to the respective folder you can try this

find . -name "*.zip" | while read filename; do unzip -o -d "`dirname "$filename"`" "$filename"; done;

A multi-processed version for systems that can handle high I/O:

find . -name "*.zip" | xargs -P 5 -I fileName sh -c 'unzip -o -d "$(dirname "fileName")/$(basename -s .zip "fileName")" "fileName"'
37
votes

Here's one solution that extracts all zip files to the working directory and involves the find command and a while loop:

find . -name "*.zip" | while read filename; do unzip -o -d "`basename -s .zip "$filename"`" "$filename"; done;
30
votes

A solution that correctly handles all file names (including newlines) and extracts into a directory that is at the same location as the file, just with the extension removed:

find . -iname '*.zip' -exec sh -c 'unzip -o -d "${0%.*}" "$0"' '{}' ';'

Note that you can easily make it handle more file types (such as .jar) by adding them using -o, e.g.:

find . '(' -iname '*.zip' -o -iname '*.jar' ')' -exec ...
5
votes

You could use find along with the -exec flag in a single command line to do the job

find . -name "*.zip" -exec unzip {} \;
4
votes

This works perfectly as we want:

Unzip files:

find . -name "*.zip" | xargs -P 5 -I FILENAME sh -c 'unzip -o -d "$(dirname "FILENAME")" "FILENAME"'

Above command does not create duplicate directories.

Remove all zip files:

find . -depth -name '*.zip' -exec rm {} \;
3
votes

Something like gunzip using the -r flag?....

Travel the directory structure recursively. If any of the file names specified on the command line are directories, gzip will descend into the directory and compress all the files it finds there (or decompress them in the case of gunzip ).

http://www.computerhope.com/unix/gzip.htm

1
votes

If you're using cygwin, the syntax is slightly different for the basename command.

find . -name "*.zip" | while read filename; do unzip -o -d "`basename "$filename" .zip`" "$filename"; done;
1
votes

I realise this is very old, but it was among the first hits on Google when I was looking for a solution to something similar, so I'll post what I did here. My scenario is slightly different as I basically just wanted to fully explode a jar, along with all jars contained within it, so I wrote the following bash functions:

function explode {
    local target="$1"
    echo "Exploding $target."
    if [ -f "$target" ] ; then
        explodeFile "$target"
    elif [ -d "$target" ] ; then
        while [ "$(find "$target" -type f -regextype posix-egrep -iregex ".*\.(zip|jar|ear|war|sar)")" != "" ] ; do
            find "$target" -type f -regextype posix-egrep -iregex ".*\.(zip|jar|ear|war|sar)" -exec bash -c 'source "<file-where-this-function-is-stored>" ; explode "{}"' \;
        done
    else
        echo "Could not find $target."
    fi
}

function explodeFile {
    local target="$1"
    echo "Exploding file $target."
    mv "$target" "$target.tmp"
    unzip -q "$target.tmp" -d "$target"
    rm "$target.tmp"
}

Note the <file-where-this-function-is-stored> which is needed if you're storing this in a file that is not read for a non-interactive shell as I happened to be. If you're storing the functions in a file loaded on non-interactive shells (e.g., .bashrc I believe) you can drop the whole source statement. Hopefully this will help someone.

A little warning - explodeFile also deletes the ziped file, you can of course change that by commenting out the last line.

0
votes

Another interesting solution would be:

DESTINY=[Give the output that you intend]

# Don't forget to change from .ZIP to .zip.
# In my case the files were in .ZIP.
# The echo were for debug purpose.

find . -name "*.ZIP" | while read filename; do
ADDRESS=$filename
#echo "Address: $ADDRESS"
BASENAME=`basename $filename .ZIP`
#echo "Basename: $BASENAME"
unzip -d "$DESTINY$BASENAME" "$ADDRESS";
done;
0
votes
unzip name_of_the_zipfile.zip

worked fine for me, after installing the zip package from Info-ZIP:

sudo apt install -y zip

The above installation is for Debian/Ubuntu/Mint. For other Linux distros, see the second reference below.

References:
http://infozip.sourceforge.net/
https://www.tecmint.com/install-zip-and-unzip-in-linux/

-1
votes

this works for me

def unzip(zip_file, path_to_extract):
    """
    Decompress zip archives recursively
    Args:
        zip_file: name of zip archive
        path_to_extract: folder where the files will be extracted
    """
    try:
        if is_zipfile(zip_file):
            parent_file = ZipFile(zip_file)
            parent_file.extractall(path_to_extract)
            for file_inside in parent_file.namelist():
                if is_zipfile(os.path.join(os.getcwd(),file_inside)):
                    unzip(file_inside,path_to_extract)
            os.remove(f"{zip_file}")
    except Exception as e:
        print(e)