Shell – Listing files from nested zip files without extracting

shell-scriptzip

I have a set of nested zip files and I need to list the file names without extracting the archives. For example:

  • Zip1.zip
    • text1
    • text2
    • Zip2.zip
      • Sample1
      • Sample2

with some shell scripting should result in a list such as

Zip1.zip
Zip1.zip/text1
Zip1.zip/text2
Zip1.zip/Zip2.zip/Sample1
Zip1.zip/Zip2.zip/Sample2

Best Answer

The unzip -p flag will pipe the uncompressed data to stdout. Unfortunately the unzip program doesn't have an option to read from stdin for some reason. Adapting the python one liner from this answer to a similar question does the trick.

eg:

unzip -p Zip1.zip Zip1/zip2.zip| python -c 'import zipfile,sys,StringIO;print "\n".join(zipfile.ZipFile(StringIO.StringIO(sys.stdin.read())).namelist())'

Added: The Java jar tool can read from stdin. stolen from this answer.

eg:

unzip -p Zip1.zip  Zip1/zip2.zip| jar -t

output:

zip2/
zip2/Sample2
zip2/Sample1

original zip file:

$ unzip -l Zip1.zip 
Archive:  Zip1.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
        0  2015-11-03 15:49   Zip1/
        5  2015-11-03 15:49   Zip1/text1
        5  2015-11-03 15:49   Zip1/text2
      474  2015-11-03 15:48   Zip1/zip2.zip
---------                     -------
      484                     4 files

Found the relevant Serverfault thread from your - comment.


This isn't a shell script, but it does what was suggested in the original question:

#!/usr/bin/python
# Usage: python list-zips.py <zipfile>

import zipfile
import io
import sys

def uz(f, parent=[]):

    result = []
    try:
        zf = zipfile.ZipFile(f)
        for e in zf.namelist():
            path=parent+[e]
            if e.lower().endswith(".zip"):
                result += uz(io.BytesIO(zf.open(e).read()), path)
            else:
                result.append("/".join(path))

    except Exception as ex:
        return result

    return result

print("\n".join(uz(open(sys.argv[1], "rb"), [sys.argv[1]])))

$ python list-zips.py Zip1.zip 
Zip1.zip/text1
Zip1.zip/text2
Zip1.zip/Zip2.zip/Sample1
Zip1.zip/Zip2.zip/Sample2
Related Question