Захват стандартного выхода и ошибки при запуске процесса
есть ли ошибка в Powershell при доступе к StandardError
и StandardOutput
свойства?
если я запускаю следующее, Я не получаю вывода
$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
6 ответов:
вот так
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 params (это поймало меня на некоторое время)
у меня также была эта проблема, и в итоге я использовал код Andys для создания функции для очистки, когда нужно запустить несколько команд, он вернет коды stderr, stdout и exit в качестве объектов. Одна вещь, чтобы отметить, что функция не будет принимать .\ в пути должны использоваться полные пути.
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
важно:
мы использовали функцию как обеспечено выше 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. WaitForExit перед P. StandardError.ReadToEnd и дочерний процесс записывает достаточно текста, чтобы заполнить перенаправленный поток. Родительский процесс будет бесконечно ждать завершения дочернего процесса. Дочерний процесс будет бесконечно ждать, пока родитель прочитает из полного потока StandardError.
EDIT: добавлена отсутствующая фигурная скобка в конце блока Try.
вот моя версия функции, которая возвращает стандартную систему.Диагностика.Процесс с 3 новыми свойствами
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 }