[powershell] Start-Process로 표준 출력 및 오류 캡처

및 속성에 Start-Process액세스 할 때 PowerShell의 명령에 버그가 있습니까?StandardErrorStandardOutput

다음을 실행하면 출력이 표시되지 않습니다.

$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 및 종료 코드를 객체로 반환합니다. 한 가지주의 할 점 : 함수는 .\경로에서 허용되지 않습니다 . 전체 경로를 사용해야합니다.

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"


답변

중대한:

위에서 제공 한 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 스트림에서 읽을 때까지 무기한 대기합니다.


답변

저는 Andy ArismendiLPG의 사례에 정말 문제가있었습니다 . 항상 다음을 사용해야합니다.

$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


답변

다음은 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