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


112

및 속성에 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

5
이 특정한 경우에 정말로 Start-process가 필요합니까? ... $process= ping localhost # 출력을 프로세스 변수에 저장합니다.
mjsr

1
진실. 나는 반환과 인수를 처리하는 더 깨끗한 방법을 찾고 있었다. 나는 당신이 보여준 것처럼 대본을 작성했습니다.
jzbruno

답변:


128

그것이 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

7
나는 당신의 대답을 받아들이고 있습니다. 나는 그들이 사용하지 않는 속성을 만들지 않았 으면 좋겠습니다. 매우 혼란 스럽습니다.
jzbruno

6
당신이 과정이 방법을 실행에 문제가있는 경우, 여기 허용 대답을 참조 stackoverflow.com/questions/11531068/... WaitForExit에 약간의 수정을 가지고 있으며, StandardOutput.ReadToEnd있는
랄프 Willgoss

3
u는 -verb runAs를 사용할 때 -NoNewWindow 또는 리디렉션 옵션을 허용하지 않습니다
Maverick

15
이 코드는 StdErr과 StdOut이 동 기적으로 끝까지 읽혀 지므로 일부 조건에서 교착 상태가됩니다. msdn.microsoft.com/en-us/library/…
codepoke

8
@codepoke-그보다 약간 더 나쁩니다. 먼저 WaitForExit 호출을 수행하기 때문에 그중 하나만 리디렉션하더라도 스트림 버퍼가 가득 차면 교착 상태가 될 수 있습니다 (프로세스까지 읽기를 시도하지 않기 때문에 종료되었습니다)
James Manning

20

질문에 주어진 코드에서 시작 변수의 ExitCode 속성을 읽는 것이 작동해야한다고 생각합니다.

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

(귀하의 예에서 -PassThru와 같이) 및 -Wait매개 변수 를 추가해야합니다 (잠시 동안 저를 잡았습니다).


argumentlist에 변수가 있으면 어떻게됩니까? 확장되지 않는 것 같습니다.
OO

1
인수 목록을 따옴표로 묶습니다. 작동할까요? ... $ 과정 = 시작 - 프로세스 -FilePath 핑 -ArgumentList "-t 로컬 호스트 -n 1"-NoNewWindow -PassThru 잠깐
JJones

powershell 창에 출력을 표시하고 로그 파일에 기록하는 방법은 무엇입니까? 가능할까요?
Murali Dhar Darshan

-NoNewWindow함께 사용할 수 없음-Verb runAs
Dragas

11

나는 또한이 문제가 있었고 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"

좋은 생각이지만 구문이 작동하지 않는 것 같습니다. 매개 변수 목록이 param ([type] $ ArgumentName) 구문을 사용하지 않아야합니까? 이 함수에 예제 호출을 추가 할 수 있습니까?
Lockszmith

"참고 : 함수는 경로에서. \를 허용하지 않습니다. 전체 경로를 사용해야합니다.": 다음을 사용할 수 있습니다.> $ pinfo.FileName = Resolve-Path $ commandPath
Lupuz

9

중대한:

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


3
이 코드는 MSDN에 대한 링크에서도 설명하는 ReadToEnd ()에 대한 동기 호출로 인해 여전히 교착 상태가됩니다.
bergmeister

1
이것은 이제 내 문제를 해결 한 것 같습니다. 나는 그것이 왜 멈추었는지 완전히 이해하지 못했지만, 빈 stderr가 프로세스를 완료하는 것을 막은 것 같습니다. 이상하게도 오랫동안 작동했지만 Xmas 직전에 갑자기 실패하기 시작하여 많은 Java 프로세스가 중단되었습니다.
rhellem

8

저는 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

"$ p.WaitForExit () 전에 항상 $ p.StandardOutput.ReadToEnd ()를 사용해야합니다."라는 내용을 어디에서 읽었습니까? 버퍼에 소진 된 출력이있는 경우 나중에 더 많은 출력을 따라 실행 라인이 WaitForExit에 있고 프로세스가 완료되지 않은 경우 누락됩니다 (이후 더 많은 stderr 또는 stdout을 출력 함) ....
CJBS

위의 의견과 관련하여 나중에 큰 출력의 경우 교착 상태 및 버퍼 오버플로에 대한 허용 된 답변에 대한 의견을 보았지만 그 외에도 버퍼를 끝까지 읽었 기 때문에 프로세스를 의미하지는 않습니다. 완료되었으므로 누락 된 더 많은 출력이있을 수 있습니다. 내가 뭔가를 놓치고 있습니까?
CJBS 2017

@CJBS : "버퍼가 끝까지 읽 혔다 고해서 프로세스가 완료 되었다는 의미가 아닙니다 . " 라는 의미입니다. 사실 이것이 교착 상태가 될 수있는 이유입니다. "끝까지"읽는 것은 " 지금 거기있는 것을 읽는다"는 의미가 아닙니다 . 읽기 시작을 의미하고 스트림이 닫힐 때까지 멈추지 마십시오. 프로세스가 종료되는 것과 같습니다.
Peter Duniho

0

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

0

다음은 다른 powershell 프로세스에서 출력을 얻는 복잡한 방법입니다.

start-process -wait -nonewwindow powershell 'ps | Export-Clixml out.xml'; import-clixml out.xml
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.