web-dev-qa-db-ja.com

Start-Processで標準出力とエラーをキャプチャする

StandardErrorおよびStandardOutputプロパティにアクセスするときに、PowerShellのStart-Processコマンドにバグはありますか?

次を実行すると、出力が得られません。

$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
89
jzbruno

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
112
Andy Arismendi

質問で与えられたコードでは、開始変数のExitCodeプロパティの読み取りが機能するはずだと思います。

$process = Start-Process -FilePath ping -ArgumentList localhost -NoNewWindow -PassThru -Wait
$process.ExitCode

(例のように)-PassThruおよび-Waitパラメーターを追加する必要があることに注意してください(これはしばらくの間私を見つけました)。

16
JJones

私もこの問題を抱えていたため、 Andyのコード を使用して、複数のコマンドを実行する必要があるときにクリーンアップする関数を作成しました。

オブジェクトとしてstderr、stdout、および終了コードを返します。注意すべき点が1つあります。関数はパスに.\を受け入れません。完全なパスを使用する必要があります。

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
    }
}

使用方法は次のとおりです。

$DisableACMonitorTimeOut = Execute-Command -commandTitle "Disable Monitor Timeout" -commandPath "C:\Windows\System32\powercfg.exe" -commandArguments " -x monitor-timeout-ac 0"
11
LPG

私は本当にそれらの例で問題を抱えていました Andy Arismendiから および LPGから 。常に使用する必要があります:

$stdout = $p.StandardOutput.ReadToEnd()

呼び出す前に

$p.WaitForExit()

完全な例は次のとおりです。

$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
6
Rainer

重要:

上記で提供されている関数を使用しています by 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
  }
}

この問題の詳細については、 MSDN をご覧ください。

親プロセスがp.StandardError.ReadToEndの前にp.WaitForExitを呼び出し、子プロセスがリダイレクトされたストリームを満たすのに十分なテキストを書き込む場合、デッドロック状態が発生する可能性があります。親プロセスは、子プロセスが終了するまで無期限に待機します。子プロセスは、親が完全なStandardErrorストリームから読み取るまで無期限に待機します。

6
pserranne

ここに、3つの新しいプロパティを持つ標準のSystem.Diagnostics.Processを返す関数のバージョンがあります

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.WindowStyle = 'Hidden'
        $pinfo.CreateNoWindow = $True
        $pinfo.Arguments = $commandArguments
        $p = New-Object System.Diagnostics.Process
        $p.StartInfo = $pinfo
        $p.Start() | Out-Null
        $stdout = $p.StandardOutput.ReadToEnd()
        $stderr = $p.StandardError.ReadToEnd()
        $p.WaitForExit()
        $p | Add-Member "commandTitle" $commandTitle
        $p | Add-Member "stdout" $stdout
        $p | Add-Member "stderr" $stderr
    }
    Catch {
    }
    $p
}
0
Anabela Mazurek

別のpowershellプロセスから出力を取得するための巧妙な方法は次のとおりです。

start-process -wait -nonewwindow powershell 'ps | Export-Clixml out.xml'; import-clixml out.xml
0
js2010