2

My command line is this (powershell):

$7z ="`"c:\Program Files\7-Zip\7z.exe`""
&$7z a -r -ttar -bd -so . | &$7z a -r -txz -bd $archive -si

The produced archive file indeed contains a tar file, but that tar file is corrupt.

Note, that breaking the pipe into two commands works correctly:

&$7z a -r -ttar -bd ${archive}.tmp .
&$7z a -r -txz -bd $archive ${archive}.tmp

The produced archive is perfectly valid.

So, what is wrong with my pipeline?

(I am using Powershell)

2 Answers 2

4

Nothing is wrong with your pipeline it is the way that the pipeline works that's causing the error.

PowerShell pipe works in an asynchronous way. Meaning that output of the first command is available to the second command immediately one object at the time even if the first one has not finished executing, See here.

Both Unix and PowerShell pipes operate in the same way. The reason why you might be seeing a difference from Unix to PowerShell is the way in which they go about it is different.

Unix passes Strings between the commands. Where as a Powershell pipe will pass full-fledged .net object between commands. This difference in the data type being past between command will be why it works on unix and not in PowerShell. If 7z.exe can not huddle these .net objects correctly the files will be come corrupt, See here.

3
  • I do not see why this should be a problem. Isn't it how pipes work in unix as well? And yet tar cf - A | gzip -9 > B.tar.gz is perfectly valid there. And as far as I know the content becomes available on the pipe as soon as it is produced and continues to flow on the pipe until the pipe is closed by the source.
    – mark
    Commented Apr 29, 2015 at 14:28
  • So all is lost? Or is there a way to make the pipe work with 7-zip in Powershell after all?
    – mark
    Commented Apr 30, 2015 at 14:24
  • @mark It does make a difference because 7z, not being a PowerShell command, expects plain data on STDIN and not .NET objects. See my answer for a possible solution.
    – sschuberth
    Commented Sep 21, 2015 at 8:22
1

Try adding | %{ "$_" } in between the pipes like

&$7z a -r -ttar -bd -so . | %{ "$_" } | &$7z a -r -txz -bd $archive -si

The point is that the second call to 7z expects unmodified data on STDIN, but PowerShell is converting the output from the first call to 7z to (multiple) (string) objects. % is an alias for foreach-object, so what the additional command does is to loop over each object and convert it to a plain string before passing it on to the second call to 7z.

Edit: Reading through PowerShell’s Object Pipeline Corrupts Piped Binary Data it looks to me now as if my suggestion would not work, and there's also no way to fix it. Well, other than wrapping the whole pipeline into a cmd /c "..." call to make cmd and not PowerShell handle the pipeline.

Edit2: I also was trying this solution from the PowerShell Cookbook, but it was very slow.

In the end, I created a .cmd script with the 7z pipes that I'm calling from my PowerShell script.

1
  • 1
    Thanks for the effort. What a blunder from the PowerShell part.
    – mark
    Commented Sep 21, 2015 at 14:17

Not the answer you're looking for? Browse other questions tagged or ask your own question.