11

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?

Raffaeu
  • 6,694
  • 13
  • 68
  • 110

1 Answers1

22

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 $_}
beatcracker
  • 6,714
  • 1
  • 18
  • 41
  • 6
    Just a quick comment. Do not think about calling `WaitForExit` on the process, because then no output is generated until the process has finished. – Gabor Csardi Sep 13 '16 at 12:59
  • 2
    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! – Mitten.O Sep 19 '19 at 07:00
  • 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. – Omglolyes Nov 09 '22 at 13:29