13

I am using a PowerShell script to execute a console application and I am trying to redirect the standard output and the standard error from there. The code I am using is the following:

$ProcessInfo = New-Object System.Diagnostics.ProcessStartInfo 
$ProcessInfo.FileName = "myExe.exe"
$ProcessInfo.Arguments = "bla bla bla"
$ProcessInfo.RedirectStandardError = $true 
$ProcessInfo.RedirectStandardOutput = $true 
$ProcessInfo.UseShellExecute = $false 
$Process = New-Object System.Diagnostics.Process 
$Process.StartInfo = $ProcessInfo 

$Process.Start() | Out-Null 
$output = $Process.StandardOutput.ReadToEnd() 
$errors = $Process.StandardError.ReadToEnd()
$Process.WaitForExit() 
$output 
$errors 

return $Process.ExitCode

So far so good, if I have an error I can see it redirected into my PowerShell console and if I have output it is also redirected. The issue is that this process takes 10 minutes and in the meantime we have no clue of what is going on.

Is there any way in PowerShell I can stream the content of the Output and the Error while the process is running? In pure .NET we can subscribe to events of the Process class, can I do the same in PowerShell?

1 Answer 1

24

Is there any way in PowerShell I can stream the content of the Output and the Error while the process is running? In pure .NET we can subscribe to events of the Process class, can I do the same in PowerShell?

Sure you can! What you need is an Object Events:

An object event is a .Net object that not only has the usual Properties and Methods in the object, but also has another member called Event, which you can register a subscription on using Register-ObjectEvent

Here is slightly modified example from the the PowerShell forums. It will output data from the ping command asynchronously (at least from the script point of view):

# Setup stdin\stdout redirection
$StartInfo = New-Object System.Diagnostics.ProcessStartInfo -Property @{
                FileName = 'ping.exe'
                Arguments = '-t 127.0.0.1'
                UseShellExecute = $false
                RedirectStandardOutput = $true
                RedirectStandardError = $true
            }

# Create new process
$Process = New-Object System.Diagnostics.Process

# Assign previously created StartInfo properties
$Process.StartInfo = $StartInfo

# Register Object Events for stdin\stdout reading
$OutEvent = Register-ObjectEvent -Action {
    Write-Host $Event.SourceEventArgs.Data
} -InputObject $Process -EventName OutputDataReceived

$ErrEvent = Register-ObjectEvent -Action {
    Write-Host $Event.SourceEventArgs.Data
} -InputObject $Process -EventName ErrorDataReceived

# Start process
[void]$Process.Start()

# Begin reading stdin\stdout
$Process.BeginOutputReadLine()
$Process.BeginErrorReadLine()

# Do something else while events are firing
do
{
    Write-Host 'Still alive!' -ForegroundColor Green
    Start-Sleep -Seconds 1
}
while (!$Process.HasExited)

# Unregister events
$OutEvent.Name, $ErrEvent.Name |
    ForEach-Object {Unregister-Event -SourceIdentifier $_}
Sign up to request clarification or add additional context in comments.

3 Comments

Just a quick comment. Do not think about calling WaitForExit on the process, because then no output is generated until the process has finished.
Note that unregistering the events is important. Without it, I had some of the events occur even seconds after the process had exited and even after the script itself was finished. The Write-Host:s in my script would print their outputs after the prompt in the command line!
Can I also live capture these events, instead of sending them to output stream? I tried $m = $Process.BeginOutputReadLine(), but that doesn't seem to work.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.