Capturing standard out and error with Start-Process
Solution 1
That's how Start-Process
was designed for some reason. Here's a way to get it without sending to file:
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = "ping.exe"
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false
$pinfo.Arguments = "localhost"
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start() | Out-Null
$p.WaitForExit()
$stdout = $p.StandardOutput.ReadToEnd()
$stderr = $p.StandardError.ReadToEnd()
Write-Host "stdout: $stdout"
Write-Host "stderr: $stderr"
Write-Host "exit code: " + $p.ExitCode
Solution 2
In the code given in the question, I think that reading the ExitCode property of the initiation variable should work.
$process = Start-Process -FilePath ping -ArgumentList localhost -NoNewWindow -PassThru -Wait
$process.ExitCode
Note that (as in your example) you need to add the -PassThru
and -Wait
parameters (this caught me out for a while).
Solution 3
IMPORTANT:
We have been using the function as provided above by LPG.
However, this contains a bug you might encounter when you start a process that generates a lot of output. Due to this you might end up with a deadlock when using this function. Instead use the adapted version below:
Function Execute-Command ($commandTitle, $commandPath, $commandArguments)
{
Try {
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = $commandPath
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false
$pinfo.Arguments = $commandArguments
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start() | Out-Null
[pscustomobject]@{
commandTitle = $commandTitle
stdout = $p.StandardOutput.ReadToEnd()
stderr = $p.StandardError.ReadToEnd()
ExitCode = $p.ExitCode
}
$p.WaitForExit()
}
Catch {
exit
}
}
Further information on this issue can be found at MSDN:
A deadlock condition can result if the parent process calls p.WaitForExit before p.StandardError.ReadToEnd and the child process writes enough text to fill the redirected stream. The parent process would wait indefinitely for the child process to exit. The child process would wait indefinitely for the parent to read from the full StandardError stream.
Solution 4
I also had this issue and ended up using Andy's code to create a function to clean things up when multiple commands need to be run.
It'll return stderr, stdout, and exit codes as objects. One thing to note: the function won't accept .\
in the path; full paths must be used.
Function Execute-Command ($commandTitle, $commandPath, $commandArguments)
{
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = $commandPath
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false
$pinfo.Arguments = $commandArguments
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start() | Out-Null
$p.WaitForExit()
[pscustomobject]@{
commandTitle = $commandTitle
stdout = $p.StandardOutput.ReadToEnd()
stderr = $p.StandardError.ReadToEnd()
ExitCode = $p.ExitCode
}
}
Here's how to use it:
$DisableACMonitorTimeOut = Execute-Command -commandTitle "Disable Monitor Timeout" -commandPath "C:\Windows\System32\powercfg.exe" -commandArguments " -x monitor-timeout-ac 0"
Solution 5
I really had troubles with those examples from Andy Arismendi and from LPG. You should always use:
$stdout = $p.StandardOutput.ReadToEnd()
before calling
$p.WaitForExit()
A full example is:
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = "ping.exe"
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false
$pinfo.Arguments = "localhost"
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start() | Out-Null
$stdout = $p.StandardOutput.ReadToEnd()
$stderr = $p.StandardError.ReadToEnd()
$p.WaitForExit()
Write-Host "stdout: $stdout"
Write-Host "stderr: $stderr"
Write-Host "exit code: " + $p.ExitCode
jzbruno
Updated on July 10, 2022Comments
-
jzbruno almost 2 years
Is there a bug in PowerShell's
Start-Process
command when accessing theStandardError
andStandardOutput
properties?If I run the following I get no output:
$process = Start-Process -FilePath ping -ArgumentList localhost -NoNewWindow -PassThru -Wait $process.StandardOutput $process.StandardError
But if I redirect the output to a file I get the expected result:
$process = Start-Process -FilePath ping -ArgumentList localhost -NoNewWindow -PassThru -Wait -RedirectStandardOutput stdout.txt -RedirectStandardError stderr.txt
-
mjsr over 12 yearsIn this specific case do you really need Start-process?...
$process= ping localhost
# would save the output in the process variable. -
jzbruno over 12 yearsTrue. I was looking for a cleaner way to handle return and arguments. I ended up writing the script like you showed.
-
scuba88 about 2 years@mjsr Any way to get the output and ExitCode doing it without Start-process? I need to know if the command succeeded, but would be nice to pass through the output for error message.
-
-
jzbruno over 12 yearsI am accepting your answer. I wish they wouldn't have created properties that aren't used, it is very confusing.
-
Andy Arismendi over 12 years@jzbruno The PowerShell team didn't create the StandardOutput/StandardError properties... They are part of the underlying System.Diagnostics.Process object . However they are only available when the
UseShellExecute
property is set to false. So it depends on how the PowerShell team implementedStart-Process
behind the scenes... Unfortunately I can't look at the source code :-( -
Ralph Willgoss over 11 yearsIf you have trouble running a process this way, see accepted answer here stackoverflow.com/questions/11531068/…, which has a slight modification to the WaitForExit and StandardOutput.ReadToEnd
-
Maverick over 11 yearsWhen u use the -verb runAs it does not allow theh -NoNewWindow or the Redirection Options
-
codepoke about 11 yearsThis code will deadlock under some conditions due to both StdErr and StdOut being synchronously read to the end. msdn.microsoft.com/en-us/library/…
-
James Manning almost 11 years@codepoke - it's slightly worse than that - since it does the WaitForExit call first, even if it only redirected one of them, it could deadlock if the stream buffer gets filled up (since it doesn't attempt to read from it until the process has exited)
-
Kiquenet over 9 yearsI'm pretty sure Windows also won't allow you to redirect standard input/output/error across the admin/non-admin security boundary. You'll have to find a different way to get output from the program running as admin - Reference: stackoverflow.com/a/8690661 Any final solution with full source code sample application ? IMHO, better samples for minimize learning curve are real applications with full source code and good patterns.
-
Lockszmith over 8 yearsGood idea, but it seems the syntax isn't working for me. Shouldn't the parameter list use the param( [type]$ArgumentName ) syntax? can you add an example call to this function?
-
O.O over 7 yearsWhat if argumentlist contains a variable? It doesn't seem to expand.
-
JJones over 7 yearsyou would put the argument list in quotes. Would that work ? ... $process = Start-Process -FilePath ping -ArgumentList " -t localhost -n 1" -NoNewWindow -PassThru -Wait
-
Rosberg Linhares over 7 yearsWhat if I don't want to wait for the process to end?
-
crokusek about 7 yearsI wish stdout and stderr where merged line by line in time as the dos redirect would do. Also echoed live to console would be nice.
-
Manfred about 7 years@RosbergLinhares in case you want to see the output before the process exits, this answer may help: stackoverflow.com/a/14061481/411428
-
bergmeister over 6 yearsThis code still deadlocks due to the synchronous call to ReadToEnd(), which your link to MSDN describes as well.
-
CJBS over 6 yearsWhere did you read that "You should always use: $p.StandardOutput.ReadToEnd() before $p.WaitForExit()"? If there's output on the buffer that is exhausted, following more output at a later time, that will be missed if the line of execution is on WaitForExit and the process hasn't finished (and subsequently outputs more stderr or stdout)....
-
CJBS over 6 yearsRegarding my comment above, I later saw the comments on the accepted answer regarding deadlocking and buffer overflow in cases of large output, but that aside, I would expect that just because the buffer is read to the end, it doesn't mean the process has completed, and there could thus be more output that's missed. Am I missing something?
-
rhellem over 6 yearsThis now seems to have solved my issue. I must admit that I do not fully understand why it did hang, but it seems that empty stderr blocked the process to finish. Strange thing, since it did work for a long period of time, but suddenly right before Xmas it started failing, causing a lot of Java-processes to hang.
-
dornadigital over 6 yearsAny way to set this object up and still redirect the StandardError to a file? I'm interested in how you're accessing both the ExitCode and StandardError.
-
Murali Dhar Darshan over 4 yearshow to show the output in powershell window as well as log it to a log file? Is it possible?
-
Dragas about 4 yearsCannot use
-NoNewWindow
with-Verb runAs
-
Eboubaker about 4 yearshow i can run the targeted executable as adminstrator like what start-process let's you do with runAs
-
Andy Arismendi about 4 years@ZOLDIK use
$pinfo.Verb = "runas"
-
Eboubaker about 4 years@AndyArismendi okay, using $pinfo.UseShellExecute = $true , makes it work but i can't redirect the output of the process (i have to remove the redirect part of the code)
-
Peter Duniho almost 4 years@CJBS: "just because the buffer is read to the end, it doesn't mean the process has completed" -- it does mean that. In fact, that's why it can deadlock. Reading "to the end" doesn't mean "read whatever's there now". It means start reading, and don't stop until the stream is closed, which is the same as the process terminating.
-
Lupuz almost 4 yearsRegarding "One thing to note: the function won't accept .\ in the path; full paths must be used.": You could use: > $pinfo.FileName = Resolve-Path $commandPath
-
rel.foo.fighters almost 3 yearsIf I still wants to write the output to a file, what is the correct syntax?
-
Sergio Cabral over 2 years-NoNewWindow !!! nice !! thanks
-
ashrasmun over 2 yearsWhenever I add -NoNewWindow I get a "CategoryInfo : InvalidArgument: (:) [Start-Process], ParameterBindingException FullyQualifiedErrorId : AmbiguousParameterSet,Microsoft.PowerShell.Commands.StartProcessCommand" error. Without it the underlying powershell script works.
-
STLDev over 2 yearsThis script works, except that output generated by the external process can be displayed in an out-of-order fashion. I built a small console app that simply echos out the parameters passed into it, in the order they appear on its command-line. When I execute it via this script, the output generated is displayed out of order.
-
Paul Williams almost 2 years@STLDev - you're correct. Please see my updated answer. In our application we were only using it for visual monitoring as the job ran, not capturing output, so it tolerable.
-
Mike almost 2 yearsyet another bookmark in my "why windows sucks" folder