简体   繁体   中英

Executing external command from PowerShell is not accepting a parameter

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?

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()

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. Ie, -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;
}

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM