Powershell: Capturing standard out and error with Process object
Solution 1
The docs on the RedirectStandardError
property suggests that it is better to put the WaitForExit()
call after the ReadToEnd()
call. The following works correctly for me:
$psi = New-object System.Diagnostics.ProcessStartInfo
$psi.CreateNoWindow = $true
$psi.UseShellExecute = $false
$psi.RedirectStandardOutput = $true
$psi.RedirectStandardError = $true
$psi.FileName = 'ipconfig.exe'
$psi.Arguments = @("/a")
$process = New-Object System.Diagnostics.Process
$process.StartInfo = $psi
[void]$process.Start()
$output = $process.StandardOutput.ReadToEnd()
$process.WaitForExit()
$output
Solution 2
Small variation so that you can selectively print the output if needed. As in if your looking just for error or warning messages and by the way Keith you saved my bacon with your response...
$psi = New-object System.Diagnostics.ProcessStartInfo
$psi.CreateNoWindow = $true
$psi.UseShellExecute = $false
$psi.RedirectStandardOutput = $true
$psi.RedirectStandardError = $true
$psi.FileName = 'robocopy'
$psi.Arguments = @("$HomeDirectory $NewHomeDirectory /MIR /XF desktop.ini /XD VDI /R:0 /W:0 /s /v /np")
$process = New-Object System.Diagnostics.Process
$process.StartInfo = $psi
[void]$process.Start()
do
{
$process.StandardOutput.ReadLine()
}
while (!$process.HasExited)
Solution 3
Here is a modification to paul's answer, hopefully it addresses the truncated output. i did a test using a failure and did not see truncation.
function Start-ProcessWithOutput
{
param ([string]$Path,[string[]]$ArgumentList)
$Output = New-Object -TypeName System.Text.StringBuilder
$Error = New-Object -TypeName System.Text.StringBuilder
$psi = New-object System.Diagnostics.ProcessStartInfo
$psi.CreateNoWindow = $true
$psi.UseShellExecute = $false
$psi.RedirectStandardOutput = $true
$psi.RedirectStandardError = $true
$psi.FileName = $Path
if ($ArgumentList.Count -gt 0)
{
$psi.Arguments = $ArgumentList
}
$process = New-Object System.Diagnostics.Process
$process.StartInfo = $psi
[void]$process.Start()
do
{
if (!$process.StandardOutput.EndOfStream)
{
[void]$Output.AppendLine($process.StandardOutput.ReadLine())
}
if (!$process.StandardError.EndOfStream)
{
[void]$Error.AppendLine($process.StandardError.ReadLine())
}
Start-Sleep -Milliseconds 10
} while (!$process.HasExited)
#read remainder
while (!$process.StandardOutput.EndOfStream)
{
#write-verbose 'read remaining output'
[void]$Output.AppendLine($process.StandardOutput.ReadLine())
}
while (!$process.StandardError.EndOfStream)
{
#write-verbose 'read remaining error'
[void]$Error.AppendLine($process.StandardError.ReadLine())
}
return @{ExitCode = $process.ExitCode; Output = $Output.ToString(); Error = $Error.ToString(); ExitTime=$process.ExitTime}
}
$p = Start-ProcessWithOutput "C:\Program Files\7-Zip\7z.exe" -ArgumentList "x","-y","-oE:\PowershellModules",$NewModules.FullName -verbose
$p.ExitCode
$p.Output
$p.Error
the 10ms sleep is to avoid spinning cpu when nothing to read.
Related videos on Youtube
Marcelo De Zen
Worked with many languages and technology stacks over the 20+ I've been designing and implementing "line of business" applications.
Updated on October 04, 2020Comments
-
Marcelo De Zen over 3 years
I want to start a Java program from PowerShell and get the results printed on the console.
I have followed the instructions of this question: Capturing standard out and error with Start-Process
But for me, this is not working as I expected. What I'm doing wrong?
This is the script:
$psi = New-object System.Diagnostics.ProcessStartInfo $psi.CreateNoWindow = $true $psi.UseShellExecute = $false $psi.RedirectStandardOutput = $true $psi.RedirectStandardError = $true $psi.FileName = 'java.exe' $psi.Arguments = @("-jar","tools\compiler.jar","--compilation_level", "ADVANCED_OPTIMIZATIONS", "--js", $BuildFile, "--js_output_file", $BuildMinFile) $process = New-Object System.Diagnostics.Process $process.StartInfo = $psi $process.Start() | Out-Null $process.WaitForExit() $output = $process.StandardOutput.ReadToEnd() $output
The
$output
variable is always empty (and nothing is printed on the console of course).-
Keith Hill almost 12 yearsConsidering PowerShell is a "shell", any reason why you just don't execute
PS> java.exe -jar tools\compiler.jar --compilation_level ...
? -
Marcelo De Zen almost 12 yearsIt actually works :) But I still don't understand why the script above doesn't print the output to console!
-
Ralph Willgoss almost 12 years@dev use Write-OutPut $output, to see what is returned via the console
-
Kiquenet over 9 yearsAny 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.
-
-
Marcelo De Zen almost 12 yearsAh, I use the | Out-Null to prevent that a "true" is printed on console.
-
Keith Hill almost 12 years@devundef Ignore the comment about piping to Out-Null. It's OK here. The pipe to Out-Null trick can be used to hold off until a Windows exe you launch has closed i.e.
Notepad.exe | Out-Null
. This doesn't apply in your case. -
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
-
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
-
Adarsha about 8 yearsYour way of repeatedly reading output buffer is the key. Otherwise processes producing large text can fill the output buffer, and program will hand. Only change that is needed is to append the read line to a string to use later.
-
PhiGamma almost 8 yearsI have encountered trouble with this approach, when the process exits due to an error, say, and ReadLine() can't catch up, so the output is truncated.
-
rollsch over 4 yearsThis a great workaround to the deadlock that occurs doing this the "proper" way.
-
ash about 2 yearsIn my testing this ends up in a deadlock scenario. The 'EndOfStream' is the culprit: stackoverflow.com/questions/2767496/…