3

I am executing the following code attempting to execute the 7z.exe command to unzip files.

$dir contains the user input of the path to the zip file which can contain spaces of course! And $dir\temp2 below is a directory that I previously created.

Get-ChildItem -path $dir -Filter *.zip |
ForEach-Object {
    $zip_path = """" + $dir + "\" + $_.name + """"
    $output = " -o""$dir\temp2"""
    &7z e $zip_path $output
}

When I execute it I get the following from 7z.exe:

7-Zip [64] 9.20  Copyright (c) 1999-2010 Igor Pavlov  2010-11-18

Processing archive: C:\test dir\test.zip


No files to process

Files: 0
Size:       0
Compressed: 50219965

If I then copy the value from $zip_path and $output to form my own cmd line it works!

For example:

7z e "c:\test dir\test.zip" -o"c:\test output"

Now, I can reproduce the same message "no files to process" I get when I execute within PowerShell by using the following cmd in cli.

7z e "c:\test dir\test.zip" o"c:\test output"

So, it seems that PowerShell is removing the dash char from my -o option. And yes, it needs to be -o"C:\test output" and not -o "c:\test output" with 7z.exe there is no space between the -o parameter and its value.

I am stumped. Am I doing something wrong or should I be doing this a different way?

3 Answers 3

3

I can never get Invoke-Expression (alias = &) to work right either, so I learned how to use a process object

    $7ZExe = (Get-Command -CommandType Application  -Name 7z )
    $7ZArgs = @(
        ('-o"{0}\{1}"' -f $dir, $_.Name), 
        ('"{0}\{1}"' -f $dir, 'temp2')
    )

    [Diagnostics.ProcessStartInfo]$7Zpsi = New-Object -TypeName:System.Diagnostics.ProcessStartInfo -Property:@{
        CreateNoWindow = $false;
        UseShellExecute = $false;
        Filename = $7ZExe.Path;
        Arguments = $7ZArgs;
        WindowStyle = 'Hidden';
        RedirectStandardOutput = $true
        RedirectStandardError = $true
        WorkingDirectory = $(Get-Location).Path
    }

    $proc = [System.Diagnostics.Process]::Start($7zpsi)
    $7ZOut = $proc.StandardOutput
    $7ZErr = $proc.StandardError
    $proc.WaitForExit()
Sign up to request clarification or add additional context in comments.

3 Comments

thank you thank you. that worked perfect. i had to flip the args array, the -o comes second and the first paramter need an "e" , like this ('e "{0}\{1}"' -f $dir, 'temp2'). thank you again
& is the call operator, which does not parse the command (see about_Operators). If you are having trouble using it, then item 1.3 or item 2 in my answer to 'call msbuild with nested quotation marks' may be useful.
This solution has the added benefit of preventing the host from swallowing stderr and stdout. For example, git.exe throws all verbose output to stderr which causes ISE to throw a RemoteException or some such nonsense.
3

I was able to duplicate the exact issue and tried numerous combinations escaping the -o switch and escaping quotes " and what not.

But as one answer mentioned Sysinternals, and I used Process Monitor to find out the format it was passing to 7z.exe. Things that work on a plain commandline doesn't work inside PowerShell the same way.

For example, if I tried to construct parameters inside PowerShell just like cmdline it would fail. I.e., -o"C:\scripts\so\new folder" doesn't work. But if you include the -o switch inside quotes then PowerShell passes the string "-oC:\scripts\so\new folder" which 7z.exe is happy to accept. So I learned that 7z.exe would accept both the formats such as

"C:\Program Files\7-zip\7z.exe" e "C:\scripts\so\new folder.zip" -o"C:\scripts\so\new folder"

and

"C:\Program Files\7-zip\7z.exe" e "C:\scripts\so\new folder.zip" "-oC:\scripts\so\new folder"

And both examples contain spaces in them.

[string]$pathtoexe = "C:\Program Files\7-Zip\7z.exe"
$dir = "C:\scripts\so"
$output = "$dir\new folder"
Get-ChildItem -path $dir -Filter *.zip | % {
    [array]$marguments = "e",$_.FullName,"-o$output";
    & $pathtoexe $marguments
}

Another approach in PowerShell V3 is to escape the PowerShell parsing feature. You can use the --% command to tell PowerShell to stop parsing any more commands like this.

$zipfile = "C:\scripts\so\newfolder.zip"
$destinationfolder = "C:\scripts\so\New Folder"
[string]$pathtoexe = "C:\Program Files\7-Zip\7z.exe"
& $pathtoexe --% e "C:\scripts\so\newfolder.zip" -o"C:\scripts\so\new folder"

Using the --% syntax, you type commands just like you would type them on the command line. I tested this logic, and it extracts files to the destination folder.

To learn more about --%, check PS> help about_parsing.

The issue with this approach is after --% it is not possible to include a variable. The solution to this issue is to just include the --% as another string variable and pass it like this. And this approach is similar to the commandline approach which wasn't working originally.

[string]$pathtoexe = "C:\Program Files\7-Zip\7z.exe"
$dir = "C:\scripts\so"
$output = "$dir\new folder"

Get-ChildItem -path $dir -Filter *.zip | % {
    $zipfile = $_.FullName;
    [string]$formatted = [System.String]::Concat("e ", """$zipfile"""," -o""$output""");
    [string]$stopparser = '--%';
    & $pathtoexe $stopparser $formatted;
}

4 Comments

This doesn't solve the problem. $Varname isn't processed after a --%, so he can't use variable names for the arguments.
@Eris I realized it after posting it and refactoring according to OP's problem.
@Eris I updated my answer after lots of tries. :) thanks for the motivation.
@Eris, I came up with a workaround with --% approach. Just include --% it as a variable.
3

Using the excellent Process Explorer from the Windows Sysinternals suite I was able to observe some very interesting behavior. I simplified your command line a little as seen below:

dir -Path $dir -Filter *.zip |
  select FullName |
  % { & 7za.exe e $_ "-o$dir\tmp" }

This was actually invoking the following command line according to Process Explorer:

C:\temp\7za.exe @{FullName="C:\temp\test.zip"} -oC:\temp\test

Telling PowerShell to expand the FullName property forces it out of the hashmap and treats it as a regular string which 7-Zip can deal with:

dir -Path $dir -Filter *.zip |
  select -ExpandProperty FullName |
  % { & 7za.exe e $_ "-o$dir\tmp" }

There may still be other issues like dealing with spaces in file names that I really didn't consider or account for, but I thought it was worth adding a note that PowerShell (v2 in this case) wasn't quite passing the parameters as you might expect.

1 Comment

+1 for mentioning Sysinternals tool. I tried different options before and was trying to include just path variable inside quotes which passing wrong strings to 7z.exe. You are awesome.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.