How to Specify Filenames Within a Zip When Creating It on the Command Line from a Pipe

How do you specify filenames within a zip when creating it on the command line from a pipe?

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

Can I specify redirects and pipes in variables?

I would do something like this (use bash -c or eval):

zipped=''
zipcommand='>'

if [ "$1" = "-z" ]
then
zipped='zipped '
filename="${filename}.zip"
zipcommand='| zip -@'
fi

echo "Creating ${zipped}patch file $filename..."

eval "svn diff $zipcommand $filename"
# this also works:
# bash -c "svn diff $zipcommand $filename"

This appears to work, but my version of zip (Mac OS X) required that i change the line:

zipcommand='| zip -@'

to

zipcommand='| zip - - >'

Edit: incorporated @DanielBungert's suggestion to use eval

PowerShell partial zip file name

It sounds like you only expect 1 zip file however I tailored this answer around the possibility of having more than 1

  • We are going to use Get-ChildItem to get any zip files from y:\ that match 'CallRecordings*.zip'
  • We then pipe these files one at a time to the ForEach-Object cmdlet where we
    • assign the extraction folder
    • unzip the file
    • and then rename the file.

$i is used to allow us different names for our renamed zip file in case there are more than 1 being processed.

    $i = 0
Get-ChildItem -Path 'Y:\' -Filter 'CallRecording*.zip' | ForEach-Object -Process {
$extractFolder = "C:\temp\$($_.BaseName)"
$_ | Expand-Archive -DestinationPath $extractFolder

# ($? tells us if the last command completed successfully)
if ($?) {
# only rename file if Expand-Archive was successful
$_ | Rename-Item -NewName ((Get-Date).AddDays(-1).ToString('yyyyMMdd') + "_$((++$i)).zip")
}
}

How to create a zip archive with PowerShell?

If you head on over to CodePlex and grab the PowerShell Community Extensions, you can use their write-zip cmdlet.

Since

CodePlex is in read-only mode in preparation for shutdown

you can go to PowerShell Gallery.

Output Filenames in a Folder to a Text File

In PowerShell:

# Get-ChildItem (gci) is PowerShell's dir equivalent.
# -File limits the output to files.
# .BaseName extracts the file names without extension.
(Get-ChildItem -File).BaseName | Out-File files.txt

Note: You can use dir in PowerShell too, where it is simply an alias of Get-ChildItem. However, to avoid confusion with cmd.exe's internal dir command, which has fundamentally different syntax, it's better to use the PowerShell-native alias, gci. To see all aliases defined for Get-ChildItem, run
Get-Alias -Definition Get-ChildItem

Note that use of PowerShell's > redirection operator - which is effectively an alias of the
Out-File cmdlet - would also result in the undesired inclusion of the output, files.txt, in the enumeration, as in cmd.exe and POSIX-like shells such as bash, because the target file is created first.

By contrast, use of a pipeline with Out-File (or Set-Content, for text input) delays file creation until the cmdlet in this separate pipeline segment is initialized[1] - and because the file enumeration in the first segment has by definition already completed by that point, due to the Get-ChildItem call being enclosed in (...), the output file is not included in the enumeration.

Also note that property access .BaseName was applied to all files returned by (Get-ChildItem ...), which conveniently resulted in an array of the individual files' property values being returned, thanks to a feature called member-access enumeration.

Character-encoding note:

  • In Windows PowerShell, Out-File / > creates "Unicode" (UTF-16LE) files, whereas Set-Content uses the system's legacy ANSI code page.

  • In PowerShell (Core) 7+, BOM-less UTF-8 is the consistent default.

The -Encoding parameter can be used to control the encoding explicitly.


[1] In the case of Set-Content, it is actually delayed even further, namely until the first input object is received, but that is an implementation detail that shouldn't be relied on.

Need to ZIP an entire directory using Node.js

I ended up using archiver lib. Works great.

Example

var file_system = require('fs');
var archiver = require('archiver');

var output = file_system.createWriteStream('target.zip');
var archive = archiver('zip');

output.on('close', function () {
console.log(archive.pointer() + ' total bytes');
console.log('archiver has been finalized and the output file descriptor has closed.');
});

archive.on('error', function(err){
throw err;
});

archive.pipe(output);

// append files from a sub-directory, putting its contents at the root of archive
archive.directory(source_dir, false);

// append files from a sub-directory and naming it `new-subdir` within the archive
archive.directory('subdir/', 'new-subdir');

archive.finalize();


Related Topics



Leave a reply



Submit