programing

Powershell: 프로세스 개체에 대한 표준 출력 및 오류 캡처

mailnote 2023. 8. 24. 22:24
반응형

Powershell: 프로세스 개체에 대한 표준 출력 및 오류 캡처

PowerShell에서 Java 프로그램을 시작하여 결과를 콘솔에 인쇄하고 싶습니다.

저는 이 질문의 지침을 따릅니다. Start-Process를 통해 표준 출력오류 캡처

하지만 저에게는, 이것이 제가 예상했던 것처럼 작동하지 않습니다.내가 뭘 잘못하고 있는 거지?

다음은 스크립트입니다.

$psi = New-object System.Diagnostics.ProcessStartInfo
$psi.CreateNoWindow = $true
$psi.UseShellExecute = $false
$psi.RedirectStandardOutput = $true
$psi.RedirectStandardError = $true
$psi.FileName = 'java.exe'
$psi.Arguments = @("-jar","tools\compiler.jar","--compilation_level",   "ADVANCED_OPTIMIZATIONS", "--js", $BuildFile, "--js_output_file", $BuildMinFile)
$process = New-Object System.Diagnostics.Process
$process.StartInfo = $psi
$process.Start() | Out-Null
$process.WaitForExit()
$output = $process.StandardOutput.ReadToEnd()
$output

$output변수는 항상 비어 있습니다(물론 콘솔에는 아무것도 인쇄되지 않습니다).

의 문서RedirectStandardError속성은 그것을 두는 것이 더 낫다는 것을 시사합니다.WaitForExit()다음에 전화합니다.ReadToEnd()호출합니다. 다음 항목이 올바르게 작동합니다.

$psi = New-object System.Diagnostics.ProcessStartInfo 
$psi.CreateNoWindow = $true 
$psi.UseShellExecute = $false 
$psi.RedirectStandardOutput = $true 
$psi.RedirectStandardError = $true 
$psi.FileName = 'ipconfig.exe' 
$psi.Arguments = @("/a") 
$process = New-Object System.Diagnostics.Process 
$process.StartInfo = $psi 
[void]$process.Start()
$output = $process.StandardOutput.ReadToEnd() 
$process.WaitForExit() 
$output

필요한 경우 출력을 선택적으로 인쇄할 수 있도록 작은 변동입니다.마치 당신이 오류나 경고 메시지를 찾고 있는 것처럼 그리고 키스가 당신의 응답으로 내 베이컨을 저장한 것처럼...

$psi = New-object System.Diagnostics.ProcessStartInfo 
$psi.CreateNoWindow = $true 
$psi.UseShellExecute = $false 
$psi.RedirectStandardOutput = $true 
$psi.RedirectStandardError = $true 
$psi.FileName = 'robocopy' 
$psi.Arguments = @("$HomeDirectory $NewHomeDirectory /MIR /XF desktop.ini /XD VDI /R:0 /W:0 /s /v /np") 
$process = New-Object System.Diagnostics.Process 
$process.StartInfo = $psi 
[void]$process.Start()
do
{
   $process.StandardOutput.ReadLine()
}
while (!$process.HasExited)

폴의 답변에 대한 수정 사항이 있습니다. 잘린 출력을 처리하기를 바랍니다.나는 실패를 이용한 테스트를 했고 절단을 보지 못했습니다.

function Start-ProcessWithOutput
{
    param ([string]$Path,[string[]]$ArgumentList)
    $Output = New-Object -TypeName System.Text.StringBuilder
    $Error = New-Object -TypeName System.Text.StringBuilder
    $psi = New-object System.Diagnostics.ProcessStartInfo 
    $psi.CreateNoWindow = $true 
    $psi.UseShellExecute = $false 
    $psi.RedirectStandardOutput = $true 
    $psi.RedirectStandardError = $true 
    $psi.FileName = $Path
    if ($ArgumentList.Count -gt 0)
    {
        $psi.Arguments = $ArgumentList
    }
    $process = New-Object System.Diagnostics.Process 
    $process.StartInfo = $psi 
    [void]$process.Start()
    do
    {


       if (!$process.StandardOutput.EndOfStream)
       {
           [void]$Output.AppendLine($process.StandardOutput.ReadLine())
       }
       if (!$process.StandardError.EndOfStream)
       {
           [void]$Error.AppendLine($process.StandardError.ReadLine())
       }
       Start-Sleep -Milliseconds 10
    } while (!$process.HasExited)

    #read remainder
    while (!$process.StandardOutput.EndOfStream)
    {
        #write-verbose 'read remaining output'
        [void]$Output.AppendLine($process.StandardOutput.ReadLine())
    }
    while (!$process.StandardError.EndOfStream)
    {
        #write-verbose 'read remaining error'
        [void]$Error.AppendLine($process.StandardError.ReadLine())
    }

    return @{ExitCode = $process.ExitCode; Output = $Output.ToString(); Error = $Error.ToString(); ExitTime=$process.ExitTime}
}


$p = Start-ProcessWithOutput "C:\Program Files\7-Zip\7z.exe" -ArgumentList "x","-y","-oE:\PowershellModules",$NewModules.FullName -verbose
$p.ExitCode
$p.Output
$p.Error

10ms 절전은 읽을 것이 없을 때 CPU가 회전하는 것을 피하기 위한 것입니다.

저는 저스틴의 솔루션을 사용하여 애쉬가 언급한 교착 시나리오를 받고 있었습니다.동일한 작업을 수행하지만 교착 상태를 방지하는 출력 및 오류 텍스트를 가져오기 위해 비동기 이벤트 핸들러에 가입하도록 스크립트를 수정했습니다.

반품 데이터를 변경하지 않고 테스트에서 교착 상태 문제를 해결한 것 같습니다.

# Define global variables used in the Start-ProcessWithOutput function.
$global:processOutputStringGlobal = ""
$global:processErrorStringGlobal = ""

# Launch an executable and return the exitcode, output text, and error text of the process.
function Start-ProcessWithOutput
{
    # Function requires a path to an executable and an optional list of arguments
    param (
        [Parameter(Mandatory=$true)] [string]$ExecutablePath,
        [Parameter(Mandatory=$false)] [string[]]$ArgumentList
    )

    # Reset our global variables to an empty string in the event this process is called multiple times.
    $global:processOutputStringGlobal = ""
    $global:processErrorStringGlobal = ""

    # Create the Process Info object which contains details about the process.  We tell it to 
    # redirect standard output and error output which will be collected and stored in a variable.
    $ProcessStartInfoObject = New-object System.Diagnostics.ProcessStartInfo 
    $ProcessStartInfoObject.FileName = $ExecutablePath
    $ProcessStartInfoObject.CreateNoWindow = $true 
    $ProcessStartInfoObject.UseShellExecute = $false 
    $ProcessStartInfoObject.RedirectStandardOutput = $true 
    $ProcessStartInfoObject.RedirectStandardError = $true 
    
    # Add the arguments to the process info object if any were provided
    if ($ArgumentList.Count -gt 0)
    {
        $ProcessStartInfoObject.Arguments = $ArgumentList
    }

    # Create the object that will represent the process
    $Process = New-Object System.Diagnostics.Process 
    $Process.StartInfo = $ProcessStartInfoObject 

    # Define actions for the event handlers we will subscribe to in a moment.  These are checking whether
    # any data was sent by the event handler and updating the global variable if it is not null or empty.
    $ProcessOutputEventAction = { 
        if ($null -ne $EventArgs.Data -and $EventArgs.Data -ne ""){
            $global:processOutputStringGlobal += "$($EventArgs.Data)`r`n"
        }
    }
    $ProcessErrorEventAction = { 
        if ($null -ne $EventArgs.Data -and $EventArgs.Data -ne ""){
            $global:processErrorStringGlobal += "$($EventArgs.Data)`r`n"
        }
    }

    # We need to create an event handler for the Process object.  This will call the action defined above 
    # anytime that event is triggered.  We are looking for output and error data received by the process 
    # and appending the global variables with those values.
    Register-ObjectEvent -InputObject $Process -EventName "OutputDataReceived" -Action $ProcessOutputEventAction
    Register-ObjectEvent -InputObject $Process -EventName "ErrorDataReceived" -Action $ProcessErrorEventAction

    # Process starts here
    [void]$Process.Start()

    # This sets up an asyncronous task to read the console output from the process, which triggers the appropriate
    # event, which we setup handlers for just above.
    $Process.BeginErrorReadLine()
    $Process.BeginOutputReadLine()
    
    # Wait for the process to exit.  
    $Process.WaitForExit()

    # We need to wait just a moment so the async tasks that are reading the output of the process can catch
    # up.  Not having this sleep here can cause the return values to be empty or incomplete.  In my testing, 
    # it seemed like half a second was enough time to always get the data, but you may need to adjust accordingly.
    Start-Sleep -Milliseconds 500


    # Return an object that contains the exit code, output text, and error text.
    return @{
        ExitCode = $Process.ExitCode; 
        OutputString = $global:processOutputStringGlobal; 
        ErrorString = $global:processErrorStringGlobal; 
        ExitTime = $Process.ExitTime
    }
}

언급URL : https://stackoverflow.com/questions/11531068/powershell-capturing-standard-out-and-error-with-process-object

반응형