powershell 管道运算符

powershell Pipe Operators

我执行了以下三个 powershell 命令。前两个命令没有返回结果,第三个命令有返回结果。这三个命令之间的主要区别在于等待参数和括号的使用。

PS C:\Users> Start-Process -FilePath 'msiexec' -ArgumentList '/I C:\Users\Downloads\Everything-1.4.1.1015.x64.msi -quiet' -PassThru | Foreach-Object -Process { $_.exitcode }

PS C:\Users> Start-Process -FilePath 'msiexec' -ArgumentList '/I C:\Users\Downloads\Everything-1.4.1.1015.x64.msi -quiet' -PassThru -wait | Foreach-Object -Process { $_.exitcode }

PS C:\Users> (Start-Process -FilePath 'msiexec' -ArgumentList '/I C:\Users\Downloads\Everything-1.4.1.1015.x64.msi -quiet' -PassThru -Wait) | Foreach-Object -Process { $_.exitcode }

1619

我测试了另外两个命令,它们之间的区别在于括号的使用。无论是否带括号,这两个命令都会返回结果。

PS C:\Users> Start-Process -FilePath 'msiexec' -ArgumentList '/I C:\Users\Downloads\Everything-1.4.1.1015.x64.msi -quiet' -PassThru | Foreach-Object -Process { $_.id }

22980

PS C:\Users> (Start-Process -FilePath 'msiexec' -ArgumentList '/I C:\Users\Downloads\Everything-1.4.1.1015.x64.msi -quiet' -PassThru -Wait) | Foreach-Object -Process { $_.id }

8064

感谢您对 -wait 参数的解释。我仍然对括号引起的差异感到困惑。希望多多回复。

The main difference between the three commands is the use of the -Wait argument and parentheses

Mathias R. Jessen 的有用评论为基础:

主要是使用Start-Process-Wait开关:

  • 没有 -WaitStart-Process 运行 异步 .

  • 如果没有 -PassThruStart-Process 会产生 无输出

虽然 -PassThru 使 Start-Process 输出代表新启动进程的 System.Diagnostics.Process 实例,除非 -Wait 也是 present 该实例的 .ExitCode 属性 还没有值,因为启动的进程通常 没有退出了.

另外,需要括号((...),因为Start-Process 将表示新启动进程的 System.Diagnostics.Process 实例发送到管道(由 ForEach-Object 接收)立即 then 等待进程退出。通过使用 (...) 分组运算符 ,您将强制等待 Start-Process 本身 退出,此时指向 Process' 实例 .ExitCode 属性 可用,感谢 -Wait.

一般来说,将命令包装在 (...) 中会强制在 fullup front 中收集其输出 - 这包括等待使其 exit - 在结果通过管道传递之前(与 streaming(逐个输出)行为相反默认情况下,发生 而命令仍然是 运行).

因此,以下方法有效 - 但 请参阅底部部分以获得更简单的替代方法

# Note: With (...), you could also pipe the output to ForEach-Object, as in
#       your question, but given that there's by definition only *one* 
#       output object, that is unnecessary.
(
  Start-Process -PassThru -Wait -FilePath 'msiexec' -ArgumentList '/i C:\Users\Downloads\Everything-1.4.1.1015.x64.msi -quiet' 
).ExitCode

从上面可以看出,使用 两个单独的语句 也可以(假设任何语句 运行完成 在执行下一个之前):

$process = Start-Process -PassThru -Wait -FilePath 'msiexec' -ArgumentList '/i C:\Users\Downloads\Everything-1.4.1.1015.x64.msi -quiet' 
$process.ExitCode 

msiexec.exe 不寻常 在于:

  • 它是一个 GUI(-subsystem) 可执行文件(相对于 console(-subsystem) 可执行文件),因此 - 即使 直接调用 - 运行 异步 .

  • 然而它报告了调用者可能感兴趣的有意义的进程退出代码,要求调用者等待其退出(终止)以便确定此退出代码。

顺便说一句:对于调用 console 应用程序,Start-Process 通常 不是 正确的工具,除了 不寻常 场景 - 参见 this answer


使用 msiexecStart-Process -PassThru -Wait 更简单的替代方法 使用 直接 通过 cmd /c 调用,确保 (a) 同步调用 和 (b) PowerShell 在其 automatic $LASTEXITCODE variable:

cmd /c 'msiexec /i C:\Users\Downloads\Everything-1.4.1.1015.x64.msi -quiet'
$LASTEXITCODE  # output misexec's exit code

注意:如果 msiexec 命令行需要包含 PowerShell 变量值,请传递一个 expandable (double-quoted) string ("...") to cmd /c instead and - as with verbatim (single-quoted) string ('...') 字符串 - 使用 embedded 双引号围绕嵌入参数,根据需要。