使用Start-Process捕获标准输出和错误

jzb*_*uno 96 powershell start-process

Start-Process访问StandardErrorStandardOutput属性时,PowerShell 命令中是否存在错误?

如果我运行以下命令,我得不到输出:

$process = Start-Process -FilePath ping -ArgumentList localhost -NoNewWindow -PassThru -Wait
$process.StandardOutput
$process.StandardError
Run Code Online (Sandbox Code Playgroud)

但是,如果我将输出重定向到文件,我得到预期的结果:

$process = Start-Process -FilePath ping -ArgumentList localhost -NoNewWindow -PassThru -Wait -RedirectStandardOutput stdout.txt -RedirectStandardError stderr.txt
Run Code Online (Sandbox Code Playgroud)

And*_*ndi 116

这就是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
Run Code Online (Sandbox Code Playgroud)

  • 由于StdErr和StdOut被同步读取到最后,此代码将在某些条件下死锁.http://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo.redirectstandardoutput.aspx (12认同)
  • 我接受你的回答.我希望他们不会创建未使用的属性,这是非常令人困惑的. (7认同)
  • 如果您以这种方式运行流程时遇到问题,请参阅此处接受的答案http://stackoverflow.com/questions/11531068/powershell-capturing-standard-out-and-error-with-process-object,其中略有修改WaitForExit和StandardOutput.ReadToEnd (5认同)
  • @codepoke - 它稍微差一些 - 因为它首先执行WaitForExit调用,即使它只重定向其中一个,如果流缓冲区被填满它也可能死锁(因为它直到进程才会尝试从中读取)退出了) (5认同)
  • 当你使用-verb runAs时,它不允许使用-NoNewWindow或Redirection Options (3认同)

JJo*_*nes 20

在问题中给出的代码中,我认为读取启动变量的ExitCode属性应该有效.

$process = Start-Process -FilePath ping -ArgumentList localhost -NoNewWindow -PassThru -Wait
$process.ExitCode
Run Code Online (Sandbox Code Playgroud)

请注意(如在您的示例中)您需要添加-PassThru-Wait参数(这让我感到有些困难).

  • 您可以将参数列表放在引号中。那行得通吗?... $process = Start-Process -FilePath ping -ArgumentList " -t localhost -n 1" -NoNewWindow -PassThru -Wait (2认同)
  • 无法将“-NoNewWindow”与“-Verb runAs”一起使用 (2认同)

小智 11

我也有这个问题,并最终使用Andy的代码创建一个函数来清理需要运行多个命令的东西.

它会将stderr,stdout和退出代码作为对象返回.有一点需要注意:函数不会.\在路径中接受; 必须使用完整路径.

Function Execute-Command ($commandTitle, $commandPath, $commandArguments)
{
    $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
    $p.WaitForExit()
    [pscustomobject]@{
        commandTitle = $commandTitle
        stdout = $p.StandardOutput.ReadToEnd()
        stderr = $p.StandardError.ReadToEnd()
        ExitCode = $p.ExitCode
    }
}
Run Code Online (Sandbox Code Playgroud)

以下是如何使用它:

$DisableACMonitorTimeOut = Execute-Command -commandTitle "Disable Monitor Timeout" -commandPath "C:\Windows\System32\powercfg.exe" -commandArguments " -x monitor-timeout-ac 0"
Run Code Online (Sandbox Code Playgroud)


小智 7

重要:

我们一直在使用LPG上面提供的功能。

但是,它包含一个错误,您在启动生成大量输出的过程时可能会遇到。因此,使用此功能时可能会陷入死锁。而是使用以下改编版本:

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
  }
}
Run Code Online (Sandbox Code Playgroud)

可以在MSDN上找到有关此问题的更多信息:

如果父进程在p.StandardError.ReadToEnd之前调用p.WaitForExit,并且子进程写入足够的文本以填充重定向的流,则可能导致死锁。父进程将无限期地等待子进程退出。子进程将无限期等待父进程从完整的StandardError流中读取。

  • 由于对ReadToEnd()的同步调用,该代码仍然死锁,您对MSDN的链接也对此进行了描述。 (2认同)

Rai*_*ner 6

我真的遇到了安迪·阿里斯门迪LPG这些例子的麻烦.你应该总是使用:

$stdout = $p.StandardOutput.ReadToEnd()
Run Code Online (Sandbox Code Playgroud)

在打电话之前

$p.WaitForExit()
Run Code Online (Sandbox Code Playgroud)

一个完整的例子是:

$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
$stdout = $p.StandardOutput.ReadToEnd()
$stderr = $p.StandardError.ReadToEnd()
$p.WaitForExit()
Write-Host "stdout: $stdout"
Write-Host "stderr: $stderr"
Write-Host "exit code: " + $p.ExitCode
Run Code Online (Sandbox Code Playgroud)

  • @CJBS:_“仅仅因为缓冲区被读到最后,并不意味着该过程已经完成”_ - 它确实意味着这一点。事实上,这就是它可能陷入僵局的原因。阅读“读到最后”并不意味着“阅读_现在_的任何内容”。这意味着开始读取,直到流关闭才停止,这与进程终止相同。 (2认同)

js2*_*010 6

这是从另一个 powershell 进程(序列化)获取标准输出的一种笨拙方法(“ps 2>&1”也会得到标准错误):

start-process -wait -nonewwindow powershell 'ps | Export-Clixml out.xml'
import-clixml out.xml
Run Code Online (Sandbox Code Playgroud)

让我强调一下-nonewwindow,至少在本地屏幕上获取标准输出和标准错误:

start-process -wait cmd '/c dir' -nonewwindow

 Volume in drive C is Windows
 Volume Serial Number is 2AC6-626F

 Directory of C:\users\me\foo

11/24/2022  11:40 AM    <DIR>          .
11/24/2022  11:40 AM    <DIR>          ..
11/24/2022  11:40 AM               330 file.json
               1 File(s)            330 bytes
               2 Dir(s)  25,042,915,328 bytes free
Run Code Online (Sandbox Code Playgroud)
start-process -wait cmd '/c dir foo' -nonewwindow

 Volume in drive C is Windows
 Volume Serial Number is 2AC6-626F

 Directory of C:\users\me\foo

File Not Found
Run Code Online (Sandbox Code Playgroud)

  • -没有新窗口!好的 !!谢谢 (2认同)