Powershell: Capturing standard out and error with Process object
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
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)
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.