如何从 Start-Process 获取输出
如何从 Start-Process 获取输出
当访问PowerShell的Start-Process
命令的StandardError
和StandardOutput
属性时,是否存在bug?
如果我运行以下命令,将不会有任何输出:
$process = Start-Process -FilePath ping -ArgumentList localhost -NoNewWindow -PassThru -Wait $process.StandardOutput $process.StandardError
但是,如果将输出重定向到文件中,我会得到预期的结果:
$process = Start-Process -FilePath ping -ArgumentList localhost -NoNewWindow -PassThru -Wait -RedirectStandardOutput stdout.txt -RedirectStandardError stderr.txt
问题的原因是在使用Start-Process函数时,当启动的进程生成大量输出时,可能会导致死锁。在原始函数中,使用了ReadToEnd()方法来读取进程的标准输出和标准错误输出,但当子进程写入足够的文本来填充重定向的流时,父进程在调用p.WaitForExit之前调用p.StandardError.ReadToEnd可能会导致死锁。为了解决这个问题,提供了一个修复的函数Execute-Command,采用异步方式读取进程的输出,避免了死锁的问题。
修复的函数Execute-Command的代码如下:
Function Execute-Command ($commandTitle, $commandPath, $commandArguments)
{
Try {
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = $commandPath
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false
$pinfo.Arguments = $commandArguments
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start() | Out-Null
[pscustomobject]@{
commandTitle = $commandTitle
stdout = $p.StandardOutput.ReadToEnd()
stderr = $p.StandardError.ReadToEnd()
ExitCode = $p.ExitCode
}
$p.WaitForExit()
}
Catch {
exit
}
}
在这个修复的函数中,使用异步方式读取进程的标准输出和标准错误输出,避免了死锁的问题。
据说,这个修复的函数已经解决了作者遇到的问题。作者承认自己并不完全理解为什么会出现死锁,但是看起来空的标准错误输出会阻塞进程的结束。作者表示奇怪的是,这个函数在很长一段时间内都正常工作,但在圣诞节之前突然开始失败,导致很多Java进程挂起。
最后,还提到了一个问题,是否有一个示例展示如何使用这个修复的函数Execute-Command,但文章中并没有给出这个示例。
问题原因:
问题出现的原因是在使用Start-Process命令时,无法获取输出结果。
解决方法:
要解决这个问题,可以通过以下步骤来获取Start-Process命令的输出结果:
1. 在命令中使用-PassThru
参数,以便将进程对象存储在变量中。
$process = Start-Process -FilePath ping -ArgumentList localhost -NoNewWindow -PassThru -Wait
2. 使用$process.WaitForExit()
等待进程执行完毕。
3. 使用$process.ExitCode
来获取进程的退出代码。
代码示例:
$process = Start-Process -FilePath ping -ArgumentList localhost -NoNewWindow -PassThru -Wait $process.ExitCode
需要注意的是,参数-PassThru
和-Wait
在命令中需要被添加,否则无法获取输出结果。
其他问题解决方法:
如果参数列表中包含变量,可以将参数列表放在引号中来展开变量。
$process = Start-Process -FilePath ping -ArgumentList " -t localhost -n 1" -NoNewWindow -PassThru -Wait
如果想要在PowerShell窗口中显示输出结果并将其记录到日志文件中,可以使用以下方法:
$process = Start-Process -FilePath ping -ArgumentList localhost -NoNewWindow -PassThru -Wait | Tee-Object -FilePath "logfile.txt"
需要注意的是,使用-NoNewWindow
参数时无法与-Verb runAs
参数一起使用。
问题:如何从Start-Process获取输出?
原因:Start-Process是为了某些原因而设计的。但是,它可以通过以下方式获取输出而不发送到文件:
$pinfo = New-Object System.Diagnostics.ProcessStartInfo $pinfo.FileName = "ping.exe" $pinfo.RedirectStandardError = $true $pinfo.RedirectStandardOutput = $true $pinfo.UseShellExecute = $false $pinfo.Arguments = "localhost" $p = New-Object System.Diagnostics.Process $p.StartInfo = $pinfo $p.Start() | Out-Null $p.WaitForExit() $stdout = $p.StandardOutput.ReadToEnd() $stderr = $p.StandardError.ReadToEnd() Write-Host "stdout: $stdout" Write-Host "stderr: $stderr" Write-Host "exit code: " + $p.ExitCode
解决方法:使用System.Diagnostics.Process对象的StandardOutput和StandardError属性来获取输出。但这些属性只在UseShellExecute属性设置为false时可用。因此,这取决于PowerShell团队在幕后如何实现Start-Process。如果无法以这种方式运行进程,请参阅此处的已接受答案,其中对WaitForExit和StandardOutput.ReadToEnd进行了轻微修改。