3

我正在执行以下代码,试图执行 7z.exe 命令来解压缩文件。

$dir 包含用户输入的 zip 文件路径,当然可以包含空格!而下面的 $dir\temp2 是我之前创建的一个目录。

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

当我执行它时,我从 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

如果我然后从 $zip_path 和 $output 复制值以形成我自己的 cmd 行,它就可以工作

例如:

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

现在,我可以通过在 cli 中使用以下 cmd 重现在 PowerShell 中执行时收到的相同消息“没有要处理的文件”。

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

因此,PowerShell 似乎正在从我的 -o 选项中删除破折号字符。的,它需要是 -o"C:\test output" 而不是 -o"c:\test output" 与 7z.exe 有 -o 参数和它的值之间没有空格。

我难住了。我做错了什么还是应该以不同的方式做这件事?

4

3 回答 3

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;
}
于 2013-09-11T16:44:56.107 回答
3

我也永远无法让 Invoke-Expression (alias = &) 正常工作,所以我学会了如何使用流程对象

    $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()
于 2013-09-11T16:03:37.870 回答
3

使用Windows Sysinternals套件中出色的Process Explorer ,我能够观察到一些非常有趣的行为。我稍微简化了你的命令行,如下所示:

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

这实际上是根据 Process Explorer 调用以下命令行:

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

告诉 PowerShell 扩展该FullName属性会强制它脱离 hashmap 并将其视为 7-Zip 可以处理的常规字符串:

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

可能还有其他问题,例如处理文件名中的空格,我真的没有考虑或考虑,但我认为值得添加注意的是 PowerShell(在这种情况下为 v2)并没有像你一样传递参数可能会期待。

于 2013-09-11T22:11:04.940 回答