了解为什么Start-Process和所有这些其他命令受限之前,首先必须了解典型EXE文件的工作方式。当运行EXE文件时,它执行其设计的任何操作;它会ping某些东西(ping),启动软件安装程序(setup),查找某个DNS记录(nslookup),等等。对于这个部分和Start-Process a
nd其他启动进程的命令效果很好。很简单。当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启动进程。
I’ll now purposefully ping a host that doesn’t resolve a DNS name and will fail. Notice the value of $LastExitCode
.
你看到退出代码不同是因为 ping 的结果不同,但你没看到流,因为原生的 PowerShell 在处理可执行文件时无法理解差异。在控制台中看到的文本是白色的;不是你熟悉和喜爱的红色错误文本。
我们可以捕获流并以几种不同的方式重定向到文件,比如使用 >
和 2>
,就像下面这样。这是老派的方式。
请注意,尽管在这种情况下,即使 ping.exe 返回退出代码 1(如上所示),该输出仍将进入标准输出。这是常见的。返回的流和退出代码完全取决于开发者,并且有许多不同的风格,不幸的是。
如果你想走“新派”的路线,你可以使用 Start-Process
并使用 -RedirectStandardError
和 -RedirectStandardOutput
参数,但它们仍然会进入文件。
Start-Process 的限制
你可以看到启动进程并返回一些常见输出并不太常见。此外,PowerShell 对流和退出代码的处理不够好。如果我要使用 Start-Process 运行 ping.exe
并希望追踪结果中发生的事情,我必须像这样每次运行可执行文件。
I still wouldn’t get my error text either!
让我们解决所有这些问题。从 PowerShell Gallery 下载我创建的一个小函数,名为 Invoke-Process
。
现在运行 ping,使用一个有效的主机,看看会发生什么。

注意我们如何获得相同的输出。 这很好!
现在使用一个无效的主机运行 ping。

现在注意,如果出现问题,我们会得到典型的红色错误文本。 Invoke-Process
在底层使用 Start-Process
来捕获 stdout、stderr 和退出码,然后将输出发送到应该发送的流。 这就是应该的方式!
可惜 PowerShell 没有使得从可执行文件中使用流和退出码变得更容易,但我想现在,既然它是开源的,我们现在可以添加进去!