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
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
}