Unzip from stdin to stdout - funzip, python

The goal is to read a zip file from stdin and uncompress to stdout.

Funzip works and is the solution I am looking for, the zip contains a single file, unfortunately funzip fails when the compressed file size is around 1GB or greater:

funzip error: invalid compressed data--length error

Update: I have discovered the above error may not indicate an actual error. Comparing two uncompressed files, one unzipped traditionally and the other through a pipe using funzip (with the above error written to stderr) the files are identical. I'd like to keep this open, so this can be confirmed or reported.

A related solution using python: Unzipping files that are flying in through a pipe

However this output is directed to a file.


Solution 1:

Repost of my answer:

BusyBox's unzip can take stdin and extract all the files to stdout. For example, when you use wget as stdin,

wget -qO- http://downloads.wordpress.org/plugin/akismet.2.5.3.zip | busybox unzip -p -

-p to extract files to pipe. The dash after is to use stdin as input.

You also can, (just like previous answer)

cat file.zip | busybox unzip -p -

But that's just redundant of unzip -p file.zip.

If your distro uses BusyBox by default (e.g. Alpine), just run unzip -p -.

Solution 2:

Simply use zcat. For example:

cat file.zip | zcat

Please note that in the example above the first part (cat file.zip) is redundant, in the sense that you can simply issue zcat file.zip and have the same results. I included it only to show that zcat is capable to read from stdin

Solution 3:

regarding stdout: unzip supports that out of the box with its -c and -p options. regarding stdin: Eric pointed out that the zip format has its directory at the end of the file, so the only way to make it streamable is to copy the input to a temporary storage. The following simple script does that:

#!/bin/bash
# usage: unzipOnTheFly [unzipArgs]
# unzipArgs: additional args to be passed to unzip
tmpFile=/tmp/unzipOnTheFly.$$.zip
cat >$tmpFile
unzip -p $tmpFile "$@"
rm $tmpFile

In the above case the solution would be

<file.zip unzipOnTheFly

If the archive contains more than one file and you want only one you can say

<file.zip unzipOnTheFly pathToOneFile