9

Say I want to add the file file.txt to foo.zip, I could just do zip -u foo.zip file.txt.

However inside the zip-file there already exist a folder with the path foo.zip/very/many/paths/ (relatively to the zip-file).

How would I add file.txt to the zip-file so it's location inside the zip-file would be foo.zip/very/many/paths/file.txt?

I could create the directory structure needed first, but isn't there an easier way?

I would normally do it like this:

$ ls
file.txt
foo.zip
$ mkdir very
$ mkdir very/many
$ mkdir very/many/paths
$ cp file.txt very/many/paths
$ zip -u foo.zip very/many/paths/file.txt
$ rm -rf very
Tyilo
  • 5,981
  • 8
    I don't know if this can be done any easier but at least you can shorten multiple mkdir commands with mkdir -p very/many/paths which creates any parent directories as needed. ;) – speakr Jan 16 '13 at 21:50
  • If you have to do it often, just create a bash script that automatise it. Taking arguments your archive and the path and file in the archive like updatezip very/many/path/file.txt file.txt – Depado Jan 21 '13 at 16:01
  • 1
    7z has an option for this 7z a yourfile.7z -sivery/many/paths/file.txt < file.txt but unfortunately, when I try this with zip, it results in an error. So this feature only works with 7z not with zip/tar :( – frostschutz Jan 29 '13 at 21:33
  • Possible duplicate of Add a file to a different path (but the question hasn't received an upvoted or accepted answer). – lgeorget May 04 '13 at 17:21

3 Answers3

2

Use Python's zipfile library?

~/wrk/tmp$ ls test.zip
ls: cannot access test.zip: No such file or directory

Ok. There is no 'test.zip' right now...

~/wrk/tmp$ python -c 'import zipfile,sys ; zipfile.ZipFile(sys.argv[1],"a").write(sys.a
rgv[2],sys.argv[3])' test.zip /etc/motd text/motd

Let's add '/etc/motd' as 'text/motd' to the nonexisting zipfile...

~/wrk/tmp$ ls -l test.zip 
-rw-r--r-- 1 yeti yeti 473 Mar 23 09:51 test.zip

The zipfile library was nice enough to create 'test.zip'.

~/wrk/tmp$ unzip -lv test.zip 
Archive:  test.zip
 Length   Method    Size  Cmpr    Date    Time   CRC-32   Name
--------  ------  ------- ---- ---------- ----- --------  ----
     357  Stored      357   0% 2014-03-20 15:47 ff1b8b7f  text/motd
--------          -------  ---                            -------
     357              357   0%                            1 file

..and it seems to contain what I wated...

Let's check it by unzipping it to stdout...

~/wrk/tmp$ unzip -p test.zip text/motd
Linux aurora 3.2.0-0.bpo.4-amd64 #1 SMP Debian 3.2.54-2~bpo60+1 x86_64

The programs included with the Debian GNU/Linux system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law.

Fine!

Now we add a 2nd file...

~/wrk/tmp$ python -c 'import zipfile,sys ; zipfile.ZipFile(sys.argv[1],"a").write(sys.argv[2],sys.argv[3])' test.zip /etc/issue otherdir/issue
~/wrk/tmp$ ls -l test.zip 
-rw-r--r-- 1 yeti yeti 605 Mar 23 09:52 test.zip
(yeti@aurora:1)~/wrk/tmp$ unzip -lv test.zip 
Archive:  test.zip
 Length   Method    Size  Cmpr    Date    Time   CRC-32   Name
--------  ------  ------- ---- ---------- ----- --------  ----
     357  Stored      357   0% 2014-03-20 15:47 ff1b8b7f  text/motd
      28  Stored       28   0% 2012-09-21 22:52 f9c3990c  otherdir/issue
--------          -------  ---                            -------
     385              385   0%                            2 files
~/wrk/tmp$ unzip -p test.zip otherdir/issue                                            Debian GNU/Linux 6.0 \n \l

~/wrk/tmp$ _

1

One way I would suggest is by unzipping, moving the file, and then re-compressing.

As an example, let's say we have this zip file:

Archive:  foo.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
        0  2013-01-30 14:38   very/
        0  2013-01-30 14:38   very/many/
        0  2013-01-30 14:38   very/many/paths/
        0  2013-01-30 14:38   very/many/paths/foo.txt
        0  2013-01-30 14:38   file.txt
---------                     -------
        0                     5 files

To unpack the file, let's create a directory in /tmp first. Then we will perform the following actions:

  1. Unzip foo.zip to our temporary directory
    d=$(mktemp -t -d foo.zip.XXXXXX) && unzip -d $d foo.zip
  2. Move the file to it's new path (relative to temp dir $d)
    mv ${d}/file.txt ${d}/very/many/paths/
  3. In subshell: cd to temp dir & Recompress everything into a new zip file
    ( cd $d && zip -r foo.zip ./* )
  4. Move the new zip file from temp dir to replace the old one
    mv ${d}/foo.zip ./
  5. Cleanup :-)
    rm -rf ${d}
  • True, but inefficient, zip is not a solid compression format and allows adding and removing contents without recompression. However, if adding files and even entire dir structure to a zip file happens frequently, something could be done differently. – orion Mar 10 '14 at 19:06
  • It is well known that the .zip format is nowhere near the highest compression ratio out there. There are much better choices such as 7z nowadays. However, the .zip format is probably the most portable format due to support being built into certain Operating Systems ;-). Plus, the OP specifically asked for a .zip format example in the question. – TrinitronX Mar 11 '14 at 21:23
-1

You may use Ant for that.

Create a file named build.xml:

<project name="zip-utils">

    <target name="add-file-to-zip">
        <fail message="Property 'zip' must be set" unless="zip" />
        <fail message="Property 'file' must be set" unless="file" />
        <fail message="Property 'path' must be set" unless="path" />

        <available file="${zip}" property="zip.exists" />
        <fail message="Zip file missing: ${zip}" unless="zip.exists" />

        <available file="${file}" property="file.exists" />
        <fail message="File missing: ${file}" unless="file.exists" />

        <dirname property="file.dir" file="${file}"/>
        <basename property="file.filename" file="${file}"/>

        <zip destfile="${zip}" update="true">
            <zipfileset fullpath="${path}" dir="${file.dir}" includes="${file.filename}"/>
        </zip>
    </target>

</project>

Then, in the same dir of build.xml, you may run it using something like:

ant add-file-to-zip -Dzip=foo.zip -Dfile=file.txt -Dpath=/very/many/paths/file.txt

tbrugz
  • 99