在 PowerShell 中使用 Start-Process:直观的进程启动

了解为什么Start-Process和所有这些其他命令受限之前,首先必须了解典型EXE文件的工作方式。当运行EXE文件时,它执行其设计的任何操作;它会ping某些东西(ping),启动软件安装程序(setup),查找某个DNS记录(nslookup),等等。对于这个部分和Start-Process and其他启动进程的命令效果很好。很简单。当EXE返回一些输出时就会出现限制。

我们可以用多种方式在PowerShell中启动进程。我们有PowerShell的Start-Process和Invoke-Expression cmdlets,我们可以直接调用可执行文件,或者使用和号(&)来调用表达式。最常见的方式是使用Start-Process,因为它可能是最直观的。PowerShell以其直观的命令命名方式而闻名,而Start-Process是一个很好的选择。然而,该命令是有限制的。

像许多其他可执行文件一样,EXE文件不仅限于Windows,它有一个标准流的概念。标准流是可执行文件返回输出的方式。这些流有三种类型;stdin,stdout和stderr。Stdin是可以传递可执行文件的流,这里我们不会关注。Stdout是可执行文件用于发送正常非错误输出的流。

在PowerShell术语中,将stdout视为Write-Output返回的内容。当发生错误时(取决于可执行文件的开发人员是否正确编写了它),可执行文件应通过stderr返回输出。这是一个专门用于返回任何错误信息的流。

这些流允许可执行文件的用户区分典型输出和错误输出。流存在已有几十年的历史,因此Windows和PowerShell都对它们了如指掌,并采用了自己的流

退出代码

退出代码、返回代码或结果代码是另一个可执行文件遵循的古老概念。退出代码是一个整数,允许可执行文件在退出时向用户发信号。

有一些标准的退出代码,程序应该遵循,例如退出代码为0表示一切正常,退出代码3010表示需要重新启动,依此类推。PowerShell可以以几种不同的方式捕获此退出代码,如使用$LastExitCode自动变量。退出代码是可执行文件向用户传达信息的另一种方法。

捕获流和退出代码

现在您了解了我们正在处理的内容,让我们使用一个示例。为了保持简单,我将使用经典的ping.exe。首先,我将ping google.com,这将返回一个成功的结果。在这里,我们不使用PowerShell启动进程。

PS C:\> ping google.com
Pinging google.com [2607:f8b0:4004:80b::200e] with 32 bytes of data:

Reply from 2607:f8b0:4004:80b::200e: time=61ms
Reply from 2607:f8b0:4004:80b::200e: time=65ms
Reply from 2607:f8b0:4004:80b::200e: time=80ms
Reply from 2607:f8b0:4004:80b::200e: time=78ms

Ping statistics for 2607:f8b0:4004:80b::200e:

Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 61ms, Maximum = 80ms, Average = 71ms

PS> $LastExitCode
0
PS> $?
True

I’ll now purposefully ping a host that doesn’t resolve a DNS name and will fail. Notice the value of $LastExitCode.

PS> ping googlllll.com

Ping request could not find host googlllll.com. Please check the name and try again.
PS> $LASTEXITCODE
1

你看到退出代码不同是因为 ping 的结果不同,但你没看到流,因为原生的 PowerShell 在处理可执行文件时无法理解差异。在控制台中看到的文本是白色的;不是你熟悉和喜爱的红色错误文本。

我们可以捕获流并以几种不同的方式重定向到文件,比如使用 >2>,就像下面这样。这是老派的方式

PS> ping googllll.com > output.msg 2> output.err
PS> Get-Content .\output.err
PS> Get-Content .\output.msg

Ping request could not find host googllll.com. Please check the name and try again.

请注意,尽管在这种情况下,即使 ping.exe 返回退出代码 1(如上所示),该输出仍将进入标准输出。这是常见的。返回的流和退出代码完全取决于开发者,并且有许多不同的风格,不幸的是。

如果你想走“新派”的路线,你可以使用 Start-Process 并使用 -RedirectStandardError-RedirectStandardOutput 参数,但它们仍然会进入文件。

Start-Process 的限制

你可以看到启动进程并返回一些常见输出并不太常见。此外,PowerShell 对流和退出代码的处理不够好。如果我要使用 Start-Process 运行 ping.exe 并希望追踪结果中发生的事情,我必须像这样每次运行可执行文件。

PS> Start-Process -FilePath -NoNewWindow ping -ArgumentList 'google.com' -RedirectStandardOutput output.txt -RedirectStandardError err.txt
PS> $LastExitCode
PS> Get-Content -Path output.txt
PS> Get-Content -Path err.txt

I still wouldn’t get my error text either!

让我们解决所有这些问题。从 PowerShell Gallery 下载我创建的一个小函数,名为 Invoke-Process

PS> Install-Script 'Invoke-Process'
PS> . Invoke-Process.ps1

现在运行 ping,使用一个有效的主机,看看会发生什么。

Invoke-Process -FilePath ping -ArgumentList ‘google.com’

注意我们如何获得相同的输出。 这很好!

现在使用一个无效的主机运行 ping。

Invoke-Process -FilePath ping -ArgumentList ‘googlddde.com’

现在注意,如果出现问题,我们会得到典型的红色错误文本。 Invoke-Process 在底层使用 Start-Process 来捕获 stdout、stderr 和退出码,然后将输出发送到应该发送的流。 这就是应该的方式!

可惜 PowerShell 没有使得从可执行文件中使用流和退出码变得更容易,但我想现在,既然它是开源的,我们现在可以添加进去!

Source:
https://adamtheautomator.com/start-process/