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
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
質問で与えられたコードでは、開始変数のExitCodeプロパティの読み取りが機能するはずだと思います。
$process = Start-Process -FilePath ping -ArgumentList localhost -NoNewWindow -PassThru -Wait
$process.ExitCode
(例のように)-PassThru
および-Wait
パラメーターを追加する必要があることに注意してください(これはしばらくの間私を見つけました)。
私もこの問題を抱えていたため、 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"
私は本当にそれらの例で問題を抱えていました 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
重要:
上記で提供されている関数を使用しています 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ストリームから読み取るまで無期限に待機します。
ここに、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
}
別のpowershellプロセスから出力を取得するための巧妙な方法は次のとおりです。
start-process -wait -nonewwindow powershell 'ps | Export-Clixml out.xml'; import-clixml out.xml