Захватить стандартную ошибку и ошибку с помощью Start-Process - PullRequest
88 голосов
/ 06 января 2012

Есть ли ошибка в команде Start-Process 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

Ответы [ 7 ]

111 голосов
/ 06 января 2012

Вот как 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
16 голосов
/ 11 декабря 2014

В коде, приведенном в вопросе, я думаю, что чтение свойства ExitCode переменной инициализации должно работать.

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

Обратите внимание, что (как в вашем примере) вам нужно добавить -PassThruи -Wait параметров (это меня некоторое время застало).

11 голосов
/ 11 ноября 2015

У меня также была эта проблема, и в итоге я использовал код Энди , чтобы создать функцию для очистки, когда нужно запустить несколько команд.

Она вернет 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"
6 голосов
/ 20 июня 2016

У меня действительно были проблемы с этими примерами от Энди Арисменди и от 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 голосов
/ 24 марта 2017

ВАЖНО:

Мы использовали функцию, указанную выше от 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.

0 голосов
/ 11 июля 2019

Вот хитрый способ получить выходные данные другого процесса powershell:

start-process -wait -nonewwindow powershell 'ps | Export-Clixml out.xml'; import-clixml out.xml
0 голосов
/ 01 августа 2018

Вот моя версия функции, которая возвращает стандартный 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
}
...