29

I'm trying to create a zip file from file contents which are being piped in, e.g.

mysql [params and query] | zip -q output.zip -

This writes the zip correctly, but when you open the zip, the file within it is called "-". Is there any way of specifying what the filename of the piped in data should be within the zip?

7 Answers7

15

You can do this.

ls | zip test.zip -@

this is done from the notion that i have 3 files in the dir.

-rw-rw-r-- 1 xxx domain users   6 Jan  7 11:41 test1.txt
-rw-rw-r-- 1 xxx domain users   6 Jan  7 11:41 test2.txt
-rw-rw-r-- 1 xxx domain users   6 Jan  7 11:41 test3.txt

and the file itself, the result is then

Archive:  test.zip
  Length     Date   Time    Name
 --------    ----   ----    ----
        6  01-07-10 11:41   test1.txt
        6  01-07-10 11:41   test2.txt
        6  01-07-10 11:41   test3.txt
 --------                   -------
       18                   3 files

From the Linux Zip Man page

If the file list is specified as -@, zip takes the list of input files from standard input.

Arto Uusikangas
  • 1,889
  • 5
  • 20
  • 33
  • 5
    Thanks - that does work when it is several files being piped in, but in this situation I'm piping in the _contents_ of a file (in this case tab delimited MySQL results). Zip is smart enough to treat it as a file, but I need to know how to tell it what to call those contents within the zip file it creates. –  Jan 07 '10 at 10:50
  • So if i understand you correctly, do you want 1 file within the zipfile with all the contents from your sql match? – Arto Uusikangas Jan 07 '10 at 10:53
  • 1
    It may not be what the OP wanted, but it's exactly what I needed :) – zelanix Nov 27 '14 at 10:59
15

You can use a named pipe, and send the request output to it, while zipping from it.

mkfifo output.txt ; mysql [params and query] > output.txt & zip output.zip -FI output.txt ; rm output.txt
Didier Trosset
  • 36,376
  • 13
  • 83
  • 122
  • Thanks, this _almost_ works: using `& zip` generates an incomplete file; it seems that zip runs while not all data has been flushed into the named pipe yet. If I use `; zip` then it's all good. – Jens Nov 04 '12 at 17:08
  • 2
    The problem with using `; zip` is that it will wait for the `mysql` command to be finished before starting to read from the named pipe. Named pipes does not have an infinite capacity. However, if the content is not very big there should be no problem. – Didier Trosset Nov 09 '12 at 09:27
9

I couldn't manage with the PHP answer (out of memory on bigger mysql dumps), and the FIFO was not working as I wanted, so my solution is to rename the file inside the ZIP archive after running the dump, using zipnote (which is included with the zip package on Debian).

mysql [params and query] | zip -q output.zip -
echo -e "@ -\n@=newname.sql" | zipnote -w output.zip
Community
  • 1
  • 1
azenet
  • 379
  • 1
  • 6
  • 14
  • 1
    Be warned that `zipnote` does not edit the file in-place, rather it creates a new temporary file for each invocation. This may be a problem your storage medium does not have enough room for two files that size, or if you're adding numerous files this way and renaming each as you go (thus rebuilding the entire archive for every addition). – Walf Jun 17 '20 at 07:33
6

From what i can gather you cannot do both with the zip command, i mean you cannot both specify the filenames and pipe the content. You can either pipe the contents and the resulting file is - or you can pipe the filenames with -@.

That does not mean that doing so is impossible using other techniques. I outline one of those below. It does mean that you have to have PHP installed and the zip extension loaded.

There could be a whole bunch of other ways to do it. But this is the easiest that I know of. Oh and it's a one-liner too.

This is a working example using PHP

echo contents | php -r '$z = new ZipArchive();$z->open($argv[1],ZipArchive::CREATE);$z->addFromString($argv[2],file_get_contents("php://stdin"));$z->close();' test.zip testfile

To run on windows just swap single and double quotes. Or just place the script in a file.

"test.zip" is the resulting Zip file, "testfile" is the name of the contents that are being piped into the command.

Result from unzip -l test.zip

Archive:  test.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
        6  01-07-2010 12:56   testfile
---------                     -------
        6                     1 file

And here is a working example using python

echo contents | python -c "import sys
import zipfile
z = zipfile.ZipFile(sys.argv[1],'w')
z.writestr(sys.argv[2],sys.stdin.read())
z.close()
" test5.zip testfile2

Result of unzip -l

Archive:  test5.zip
  Length     Date   Time    Name
 --------    ----   ----    ----
        9  01-07-10 13:43   testfile2
 --------                   -------
        9                   1 file

Result of unzip -p

contents
Peter Lindqvist
  • 10,122
  • 3
  • 41
  • 60
  • Thanks - tried that and it works perfectly! Much prefer doing this to saving out to temporary files. –  Jan 07 '10 at 12:09
  • The named pipe solution by @didier-trosset looks more acceptable since it doesn't require the presence of a whole programming language + runtime. – jc00ke Oct 31 '14 at 16:49
  • @jc00ke If you care to read the comments you will learn that there are downsides to the named pipe solution as well. I've also given the example in two different and very common languages. There are a majority of Linux systems around that carry both these runtimes. Also there is the advantage of being somewhat platform independent. – Peter Lindqvist Nov 06 '14 at 09:03
  • Neat, but it seems to read the entire file into memory, so could be a problem for a large file. The solution by @azenet avoids that. – AdamS Oct 05 '18 at 15:13
2
mysql [params and query] | python3 -c "import sys as s,os,zipfile as m;z=m.ZipFile(os.fdopen(s.stdout.fileno(),'wb'),'w');z.writestr(s.argv[1],s.stdin.read());z.close()" 'filename.sql' > output.zip

Python 3.5 added support for writing to unseekable streams.

It's not pretty but works.

166_MMX
  • 590
  • 5
  • 18
1

Just stick with the - as filename, as zip --help suggests:

The default action is to add or replace zipfile entries from list, which can include the special name - to compress standard input.

but rename it after. You can use 7zip rn for this purpose or any other compression tool.

Here an example reading from curl a plain text, compressing it with best compression and then renaming the minus file after archive is ready.

curl -q 'https://ws-export.wmcloud.org/?lang=en&page=In+Event+of+Moon+Disaster&format=txt&fonts=' | zip -9 moon.zip -
7z rn moon.zip -- - moondisaster.txt

Why double --? To pass - as filename instead of command option.

Result is a zip named moon.zip with a single moondisaster.txt file inside.

chirale
  • 1,659
  • 16
  • 20
-3

You can try:

mysql [params and query] | zip -q output.zip -@
double-beep
  • 5,031
  • 17
  • 33
  • 41
ghostdog74
  • 327,991
  • 56
  • 259
  • 343
  • no this will not work, -@ takes a list of filenames from stdin, so the output of mysql would be interpreted as list of files. – reox May 14 '14 at 10:08