Bash – Copy a File From One Zip to Another

bashfile managementlinuxscriptingzip

I have a file named 'sourceZip.zip'

This file ('sourceZip.zip') contains two files:

'textFile.txt'

'binFile.bin'


I also have a file named 'targetZip.zip'

This file ('targetZip.zip') contains one file:

'jpgFile.jpg'


In linux, what bash command shall I use to copy both files ('textFile.txt', 'binFile.bin') from the source archive ('sourceZip.zip') straight into the second archive ('targetZip.zip'), so that at the end of the process, the second archive ('targetZip.zip') will include all three files?

(ideally, this would be done in one command, using 'zip' or 'unzip')

Best Answer

Using the usual command-line zip tool, I don't think you can avoid separate extraction and update commands.

source_zip=$PWD/sourceZip.zip
target_zip=$PWD/targetZip.zip
temp_dir=$(mktemp -dt)
( cd "$temp_dir"
  unzip "$source_zip"
  zip -g "$targetZip" .
  # or if you want just the two files: zip -g "$targetZip" textFile.txt binFile.bin
)
rm -rf "$temp_dir"

There are other languages with more convenient zip file manipulation libraries. For example, Perl with Archive::Zip. Error checking omitted.

use Archive::Zip;
my $source_zip = Archive::Zip->new("sourceZip.zip");
my $target_zip = Archive::Zip->new("targetZip.zip");
for my $member ($source_zip->members()) {
          # or (map {$source_zip->memberNamed($_)} ("textFile.txt", "binFile.bin"))
    $target_zip->addMember($member);
}
$target_zip->overwrite();

Another way is to mount the zip files as directories. Mounting either of the zip files is enough, you can use zip or unzip on the other side. Avfs provides read-only support for many archive formats.

mountavfs
target_zip=$PWD/targetZip.zip
(cd "$HOME/.avfs$PWD/sourceZip.zip#" &&
 zip -g "$target_zip" .)  # or list the files, as above
umountavfs

Fuse-zip provides read-write access to zip archives, so you can copy the files with cp.

source_dir=$(mktemp -dt)
target_dir=$(mktemp -dt)
fuse-zip sourceZip.zip "$source_dir"
fuse-zip targetZip.zip "$target_dir"
cp -Rp "$source_dir/." "$target_dir" # or list the files, as above
fusermount -u "$source_dir"
fusermount -u "$target_dir"
rmdir "$source_dir" "$target_dir"

Warning: I typed these scripts directly in my browser. Use at your own risk.

Related Question