How to capture process output asynchronously in powershell?

后端 未结 5 2062
佛祖请我去吃肉
佛祖请我去吃肉 2020-12-05 11:10

I want to capture stdout and stderr from a process that I start in a Powershell script and display it asynchronously to the console. I\'ve found some documentation on doing

5条回答
  •  再見小時候
    2020-12-05 11:37

    Based on Alexander Obersht's answer I've created a function that uses timeout and asynchronous Task classes instead of event handlers. According to Mike Adelson

    Unfortunately, this method(event handlers) provides no way to know when the last bit of data has been received. Because everything is asynchronous, it is possible (and I have observed this) for events to fire after WaitForExit() has returned.

    function Invoke-Executable {
    # from https://stackoverflow.com/a/24371479/52277
        # Runs the specified executable and captures its exit code, stdout
        # and stderr.
        # Returns: custom object.
    # from http://www.codeducky.org/process-handling-net/ added timeout, using tasks
    param(
            [Parameter(Mandatory=$true)]
            [ValidateNotNullOrEmpty()]
            [String]$sExeFile,
            [Parameter(Mandatory=$false)]
            [String[]]$cArgs,
            [Parameter(Mandatory=$false)]
            [String]$sVerb,
            [Parameter(Mandatory=$false)]
            [Int]$TimeoutMilliseconds=1800000 #30min
        )
        Write-Host $sExeFile $cArgs
    
        # Setting process invocation parameters.
        $oPsi = New-Object -TypeName System.Diagnostics.ProcessStartInfo
        $oPsi.CreateNoWindow = $true
        $oPsi.UseShellExecute = $false
        $oPsi.RedirectStandardOutput = $true
        $oPsi.RedirectStandardError = $true
        $oPsi.FileName = $sExeFile
        if (! [String]::IsNullOrEmpty($cArgs)) {
            $oPsi.Arguments = $cArgs
        }
        if (! [String]::IsNullOrEmpty($sVerb)) {
            $oPsi.Verb = $sVerb
        }
    
        # Creating process object.
        $oProcess = New-Object -TypeName System.Diagnostics.Process
        $oProcess.StartInfo = $oPsi
    
    
        # Starting process.
        [Void]$oProcess.Start()
    # Tasks used based on http://www.codeducky.org/process-handling-net/    
     $outTask = $oProcess.StandardOutput.ReadToEndAsync();
     $errTask = $oProcess.StandardError.ReadToEndAsync();
     $bRet=$oProcess.WaitForExit($TimeoutMilliseconds)
        if (-Not $bRet)
        {
         $oProcess.Kill();
        #  throw [System.TimeoutException] ($sExeFile + " was killed due to timeout after " + ($TimeoutMilliseconds/1000) + " sec ") 
        }
        $outText = $outTask.Result;
        $errText = $errTask.Result;
        if (-Not $bRet)
        {
            $errText =$errText + ($sExeFile + " was killed due to timeout after " + ($TimeoutMilliseconds/1000) + " sec ") 
        }
        $oResult = New-Object -TypeName PSObject -Property ([Ordered]@{
            "ExeFile"  = $sExeFile;
            "Args"     = $cArgs -join " ";
            "ExitCode" = $oProcess.ExitCode;
            "StdOut"   = $outText;
            "StdErr"   = $errText
        })
    
        return $oResult
    }
    

提交回复
热议问题