Is it possible to move files within a zip file?

Viewed 96
example.zip/
└── example/
    ├── nice.md
    ├── tree.md
    └── diagram.md

Expected:
example.zip/
├── nice.md
├── tree.md
└── diagram.md

example.zip contains a folder with the same name. In it are files that I want to move to the root of the zip file and remove the empty directory.

I looked at the zip man page. Could not find any flags related to the issue or I could be missing something.

I tried the --copy-entries flag. This create a new zip with selected files from the existing zip but also copy over the folder hierarchy.

zip example.zip "*.md" --copy-entries --out example1.zip

I am trying to write a shell script to do this.

Is it possible to do without extracting the zip?

2 Answers

If you have (or can install) 7z (aka p7zip) you can make use of the d(delete) and rn(rename) options, eg:

$ mkdir example
$ touch example/{nice.md,tree.md,diagram.md}

$ zip -r example.zip example
  adding: example/ (stored 0%)
  adding: example/diagram.md (stored 0%)
  adding: example/nice.md (stored 0%)
  adding: example/tree.md (stored 0%)

$ unzip -l example.zip
Archive:  example.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
        0  09-15-2022 09:29   example/
        0  09-15-2022 09:29   example/diagram.md
        0  09-15-2022 09:29   example/nice.md
        0  09-15-2022 09:29   example/tree.md
---------                     -------
        0                     4 files

# rename the *.md files first and then delete the directory; if you delete
# the directory first you'll lose all files under the directory; the 7z d/rn 
# commands will generate a lot of output (not shown here)

$ 7z rn example.zip example/nice.md    nice.md
$ 7z rn example.zip example/tree.md    tree.md
$ 7z rn example.zip example/diagram.md diagram.md
$ 7z  d example.zip example

$ unzip -l example.zip
Archive:  example.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
        0  09-15-2022 09:29   diagram.md
        0  09-15-2022 09:29   nice.md
        0  09-15-2022 09:29   tree.md
---------                     -------
        0                     3 files

$ unzip example.zip
Archive:  example.zip
 extracting: diagram.md
 extracting: nice.md
 extracting: tree.md

I'm guessing in OP's real life example the names of the directories and/or files may not be known in advance; the 7z commands do work with bash variables (eg, 7z d "${zipfile}" "${dir_to_delete}"); if OP has issues dynamically processing the contents of a given *zip then I'd recommend asking a new question ...


For a large number of renames (or deletes) it looks like you can also:

Good answer. Just to be clear, 7z does not do an in-place edit on the zip file when it does the rename/delete. Under the hood it copies the old zip into a temporary file (example.zip.tmp in this instance), renaming & deleting as it does that copy. Then it deletes the original zip file and renames the temporary file, example.zip.tmp back to original filename, example.zip. For the most part this is a perfectly acceptable (and safe) approach.

Here are the relevant lines from an strace run that shows the deletion of th eoriginal example.zip file, followed by renaming the example.zip.tmp file to example.zip.

$ strace 7z rn example.zip example/tree.md    tree.md 
...
unlink("example.zip")                   = 0
rename("example.zip.tmp", "example.zip") = 0
...

Main edge condition of this approach is with very large zip files where you are strapped for disk space -- you need to have space available to store the zip file twice when it creates the temporary copy.

Related