Захват стандартных ошибок и ошибок с помощью Start-Process
Есть ли ошибка в команде PowerShell Start-Process
при доступе к свойствам 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
Ответы
Ответ 1
Вот как почему Start-Process
был разработан 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
Ответ 2
В коде, приведенном в вопросе, я думаю, что чтение свойства ExitCode переменной инициализации должно работать.
$process = Start-Process -FilePath ping -ArgumentList localhost -NoNewWindow -PassThru -Wait
$process.ExitCode
Обратите внимание, что (как в вашем примере) вам нужно добавить параметры -PassThru
и -Wait
(это меня -Wait
).
Ответ 3
У меня также была эта проблема, и в итоге я использовал код Энди, чтобы создать функцию для очистки, когда нужно выполнить несколько команд.
Он вернет 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
}
}
Вот как это использовать:
$DisableACMonitorTimeOut = Execute-Command -commandTitle "Disable Monitor Timeout" -commandPath "C:\Windows\System32\powercfg.exe" -commandArguments " -x monitor-timeout-ac 0"
Ответ 4
У меня действительно были проблемы с этими примерами от Энди Арисменди и от 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
Ответ 5
ВАЖНЫЙ:
Мы использовали функцию, как указано выше 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.
Ответ 6
Вот моя версия функции, которая возвращает стандартный System.Diagnostics.Process с 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
}
Ответ 7
Вот хитрый способ получить выходные данные другого процесса powershell:
start-process -wait -nonewwindow powershell 'ps | Export-Clixml out.xml'; import-clixml out.xml