Подтвердить что ты не робот

Захват стандартных ошибок и ошибок с помощью 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
4b9b3361

Ответ 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