2012-01-06 29 views
61

StandardErrorStandardOutputプロパティにアクセスするときにPowershellのStart-Processコマンドにバグがありますか?Start-Processで標準出力とエラーをキャプチャする

私は次のことを実行する場合、私は何も出力

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

を取得していない。しかし、私は、出力をファイルにリダイレクトする場合、私はStart-Processが何らかの理由のために設計された方法です期待される結果

$process = Start-Process -FilePath ping -ArgumentList localhost -NoNewWindow -PassThru -Wait -RedirectStandardOutput stdout.txt -RedirectStandardError stderr.txt 
+4

あなたは本当にスタートプロセスが必要なのですか?...'$ process = ping localhost'#は出力をプロセス変数に保存します。 – mjsr

+1

真。私はリターンと議論を処理するためのよりクリーンな方法を探していました。私はあなたのようにスクリプトを書くことをやめました。 – jzbruno

答えて

84

を取得します。ここでは、ファイルに送信せずにそれを得るための方法です:質問に与えられたコードで

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

私はあなたの答えを受け入れています。私は彼らが使用されていないプロパティを作成しないことを望む、それは非常に混乱しています。 – jzbruno

+1

@jzbruno PowerShellチームは、StandardOutput/StandardErrorプロパティを作成しませんでした。これらは、基礎となる[System.Diagnostics.Process](http://msdn.microsoft.com/en-us/library/system)の一部です。 diagnostics.process.aspx)オブジェクトです。ただし、UseShellExecuteプロパティがfalseに設定されている場合にのみ使用できます。したがって、PowerShellチームがバックグラウンドで「Start-Process」を実装した方法に依存します...残念ながら私はソースコードを見ることができません:-( –

+2

この方法でプロセスを実行する際に問題がある場合は、 //stackoverflow.com/questions/11531068/powershell-capturing-standard-out-and-error-with-process-objectこれはWaitForExitとStandardOutputに若干の変更が加えられています。ReadToEnd –

13

、私は開始変数の終了コードプロパティの読み取りが動作するはずだと思います。

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

(あなたの例のように)あなたが-PassThruと-waitのparamsを追加する必要があります(これはしばらくの間、私を捕まえ)

+0

引数リストに変数が含まれている場合はどうなりますか?それは拡大していないようです。 –

+1

引数リストを引用符で囲みます。それは働くだろうか? ... $ process = Start-Process -FilePath ping -ArgumentList "-t localhost -n 1" -NoNewWindow -PassThru -Wait – JJones

8

また、私はこの問題を持っていたAndysコードを使用して終了複数のコマンドを実行する必要があるときにクリーンアップする関数を作成するには、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 
    } 
} 

はここ@Andy Arismendiと@LPGから上記のこれらの例でそれ

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

良いアイデアですが、構文がうまくいかないようです。パラメータリストでparam([type] $ ArgumentName)構文を使用しないでください。この関数に呼び出しの例を追加できますか? – Lockszmith

4

私は本当に持っていた問題を使用する方法です。あなたは、常に使用する必要があります。

$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 
+0

これはデッドロックの問題を解決しますか? –

+0

どこを読んだのですか?「$ p.WaitForExit()の前に必ず$ p.StandardOutput.ReadToEnd()を使用するべきですか?実行された行がWaitForExitにあり、プロセスが終了していない(そしてその後にstderrまたはstdoutが出力された)場合は、出力がバッファに残っていて、後で出力が増えると、その行が見逃されます。 – CJBS

+0

上記の私のコメントに関しては、大きな出力の場合にデッドロックやバッファオーバーフローに関する回答をコメントで見てきましたが、バッファが最後まで読み込まれているという理由だけで、プロセスが完了したので、欠落している出力が増える可能性があります。何か不足していますか? – CJBS

3

重要:

LPGによって上記のよう私たちは、機能を使用しています。 これには、大量の出力を生成するプロセスを開始するときに遭遇する可能性のあるバグが含まれています。このため、この関数を使用するときにデッドロックが発生する可能性があります。親プロセスがp.StandardError.ReadToEnd前p.WaitForExitを呼び出す場合、デッドロック状態が発生することができます

と:この問題に関するさらなる情報は at MSDNを見つけることができます

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

:代わりに、以下のようになったバージョンを使用します子プロセスは、リダイレクトされたストリームを満たすのに十分なテキストを書き込みます。親プロセスは、子プロセスが終了するまで無期限に待機します。子プロセスは、親が完全なStandardErrorストリームから読み出すのを無限に待つことになります。

EDIT:Tryブロックの最後に中括弧が追加されました。この特定のケースで

+1

このコードは、MSDNへのリンクでも記述されているReadToEnd()への同期呼び出しのために、依然としてデッドロックします。 – bergmeister

+0

これは私の問題を解決したようです。私はなぜそれがハングしたのか完全に理解していないことを認めなければならないが、空のstderrがプロセスを終了するのを妨げているようだ。奇妙なことに、それは長い間働いていたが、Xmasが失敗し始めた直前に突然、多くのJavaプロセスがハングした。 – rhellem

関連する問題