【问题标题】:powershell Pipe Operatorspowershell 管道运算符
【发布时间】:2022-01-13 02:35:07
【问题描述】:

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

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 参数的解释。我仍然对括号造成的差异感到困惑。希望得到更多的答复。

【问题讨论】:

  • 不能在进程退出之前填充ExitCode 属性 - -Wait 导致Start-Process 等到目标进程退出后再返回
  • 命令 2 和 3 都使用 -Wait,但是命令 2 没有返回结果。
  • 请查看您发布的代码 - 只有第三个有 -Wait 开关
  • 我理解wait参数造成的差异,但我也想知道括号造成的差异。
  • 好点,@Santiago: (...) 强制等待Start-Process 作为一个整体 退出,并且(与-Wait)只有那个 i> 保证进程真正退出;相比之下,如果没有(...),新启动的进程会在它终止之前立即发送到管道,这就是为什么ForEach-Object 还不能访问.ExitCode

标签: windows powershell exit-code start-process


【解决方案1】:

三个命令的主要区别在于-Wait参数和括号的使用

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

主要需要使用Start-Process-Wait 开关

  • 没有-WaitStart-Process异步运行

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

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

另外,括号 ((...)) 也是必需的,因为Start-Process 发出代表新启动的System.Diagnostics.Process 实例处理到管道(由ForEach-Object 接收)立即,然后然后等待进程退出。通过使用(...)grouping operator,您强制等待Start-Process 本身退出,此时Process'实例.ExitCode属性是 可用,感谢-Wait

一般来说,将命令包装在(...) 中会强制完全收集其输出,预先 - 包括等待它退出 - 在结果通过管道之前(与默认的 流式传输(逐个输出)行为相反,该行为发生在 命令仍在运行时 em>)。

因此,以下方法可行 - 但请参阅底部以获取更简单的替代方法

# 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) 可执行文件相反),因此 - 即使直接调用 /em> - 异步运行

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

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


msiexecStart-Process -PassThru -Wait 一起使用的更简单的替代方法是通过cmd /c使用直接调用,这可以确保两者( a) 同步调用和 (b) PowerShell 在其automatic $LASTEXITCODE variable 中反映msiexec 的退出代码:

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 ("...") 传递给 cmd /c 并且 - 与 verbatim (single-quoted) string ('...') 字符串一样 - 使用 embedded 双引号根据需要嵌入参数。

【讨论】:

  • 当我想获取退出代码时,我必须使用括号。想获取id的时候,不管用不带括号都没有效果。
  • @jiligulu Id 是在进程 startup 上分配的,因此无论您是否等待进程退出,它都可以立即使用。这是一个简单的时间问题。
  • @jiligulu,您是对的:需要(...) 才能获得.ExitCode 值 - 请参阅我的更新。但是,正如@Mathias 指出的那样,Process 实例的 其他 属性,例如 .Id 立即可用。
猜你喜欢
  • 2022-06-29
  • 1970-01-01
  • 2011-03-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-09
相关资源
最近更新 更多