Powershell: 使用Process对象捕获标准输出和错误信息
Powershell: 使用Process对象捕获标准输出和错误信息
我想从PowerShell启动一个Java程序,并将结果打印到控制台上。
我按照这个问题的指示进行了操作:
但对我来说,这并没有按照我期望的方式工作。我做错了什么?
这是脚本:
$psi = New-object System.Diagnostics.ProcessStartInfo $psi.CreateNoWindow = $true $psi.UseShellExecute = $false $psi.RedirectStandardOutput = $true $psi.RedirectStandardError = $true $psi.FileName = 'java.exe' $psi.Arguments = @("-jar","tools\compiler.jar","--compilation_level", "ADVANCED_OPTIMIZATIONS", "--js", $BuildFile, "--js_output_file", $BuildMinFile) $process = New-Object System.Diagnostics.Process $process.StartInfo = $psi $process.Start() | Out-Null $process.WaitForExit() $output = $process.StandardOutput.ReadToEnd() $output
$output
变量总是为空(当然在控制台上也没有打印任何内容)。
这是对Paul答案的修改,希望能解决输出被截断的问题。我进行了一个失败的测试,并没有看到截断。
function Start-ProcessWithOutput { param ([string]$Path,[string[]]$ArgumentList) $Output = New-Object -TypeName System.Text.StringBuilder $Error = New-Object -TypeName System.Text.StringBuilder $psi = New-object System.Diagnostics.ProcessStartInfo $psi.CreateNoWindow = $true $psi.UseShellExecute = $false $psi.RedirectStandardOutput = $true $psi.RedirectStandardError = $true $psi.FileName = $Path if ($ArgumentList.Count -gt 0) { $psi.Arguments = $ArgumentList } $process = New-Object System.Diagnostics.Process $process.StartInfo = $psi [void]$process.Start() do { if (!$process.StandardOutput.EndOfStream) { [void]$Output.AppendLine($process.StandardOutput.ReadLine()) } if (!$process.StandardError.EndOfStream) { [void]$Error.AppendLine($process.StandardError.ReadLine()) } Start-Sleep -Milliseconds 10 } while (!$process.HasExited) #read remainder while (!$process.StandardOutput.EndOfStream) { #write-verbose 'read remaining output' [void]$Output.AppendLine($process.StandardOutput.ReadLine()) } while (!$process.StandardError.EndOfStream) { #write-verbose 'read remaining error' [void]$Error.AppendLine($process.StandardError.ReadLine()) } return @{ExitCode = $process.ExitCode; Output = $Output.ToString(); Error = $Error.ToString(); ExitTime=$process.ExitTime} } $p = Start-ProcessWithOutput "C:\Program Files\7-Zip\7z.exe" -ArgumentList "x","-y","-oE:\PowershellModules",$NewModules.FullName -verbose $p.ExitCode $p.Output $p.Error
10毫秒的休眠是为了避免在没有内容可读取时消耗CPU。
在我的测试中,这会导致死锁的情况。问题出在'EndOfStream'上:stackoverflow.com/questions/2767496/…
问题的出现原因:
在使用Powershell的Process对象捕获标准输出和错误时,有一些问题可能会导致输出被截断或程序出现死锁。这可能是由于缓冲区溢出或进程错误退出而无法及时读取输出导致的。
解决方法:
为了解决这个问题,可以采用一种小变化的方法,通过重复读取输出缓冲区的内容来避免死锁。同时,将每次读取的行追加到一个字符串中,以便以后使用。这样可以避免输出被截断的问题。
具体的解决方法如下所示:
$psi = New-object System.Diagnostics.ProcessStartInfo $psi.CreateNoWindow = $true $psi.UseShellExecute = $false $psi.RedirectStandardOutput = $true $psi.RedirectStandardError = $true $psi.FileName = 'robocopy' $psi.Arguments = @("$HomeDirectory $NewHomeDirectory /MIR /XF desktop.ini /XD VDI /R:0 /W:0 /s /v /np") $process = New-Object System.Diagnostics.Process $process.StartInfo = $psi [void]$process.Start() do { $process.StandardOutput.ReadLine() } while (!$process.HasExited)
另外,需要注意的是,在Windows系统中,无法在管理员和非管理员安全边界之间重定向标准输入/输出/错误。如果需要从以管理员身份运行的程序中获取输出,需要找到其他方法来实现。
参考链接:
- [stackoverflow.com/a/8690661](http://stackoverflow.com/a/8690661)
问题的出现原因:在使用Process对象捕获标准输出和错误时,文档中建议在调用ReadToEnd()方法之后再调用WaitForExit()方法。
解决方法:
1. 创建一个ProcessStartInfo对象,并设置其属性,包括CreateNoWindow、UseShellExecute、RedirectStandardOutput和RedirectStandardError等。
2. 创建一个Process对象,并将ProcessStartInfo对象赋给其StartInfo属性。
3. 调用Process对象的Start()方法启动进程。
4. 使用ReadToEnd()方法读取进程的标准输出,并将结果保存到变量$output中。
5. 调用WaitForExit()方法等待进程的结束。
6. 输出变量$output的值。
另外,还有一些额外的说明:
- 使用| Out-Null可以防止控制台上打印出"true"。
- 如果在跨管理员/非管理员安全边界重定向标准输入/输出/错误,Windows可能不允许。需要找到其他方法来获取以管理员身份运行的程序的输出。
- 如果感兴趣,可以在pwsh核心存储库中打开一个问题。