PowerShell - redirect executable's stderr to file or variable but still have stdout go to console
I think I have your answer. I'm working with PowerShell for a while and created several build systems. Sorry if the script is a bit long, but it works.
$dir = <your dir>$global:log = <your log file which must be in the global scope> # Not global = won't workfunction Create-Process { $process = New-Object -TypeName System.Diagnostics.Process $process.StartInfo.CreateNoWindow = $false $process.StartInfo.RedirectStandardError = $true $process.StartInfo.UseShellExecute = $false return $process}function Terminate-Process { param([System.Diagnostics.Process]$process) $code = $process.ExitCode $process.Close() $process.Dispose() Remove-Variable process return $code}function Launch-Process { param([System.Diagnostics.Process]$process, [string]$log, [int]$timeout = 0) $errorjob = Register-ObjectEvent -InputObject $process -EventName ErrorDataReceived -SourceIdentifier Common.LaunchProcess.Error -action { if(-not [string]::IsNullOrEmpty($EventArgs.data)) { "ERROR - $($EventArgs.data)" | Out-File $log -Encoding ASCII -Append Write-Host "ERROR - $($EventArgs.data)" } } $outputjob = Register-ObjectEvent -InputObject $process -EventName OutputDataReceived -SourceIdentifier Common.LaunchProcess.Output -action { if(-not [string]::IsNullOrEmpty($EventArgs.data)) { "Out - $($EventArgs.data)" | Out-File $log -Encoding ASCII -Append Write-Host "Out - $($EventArgs.data)" } } if($errorjob -eq $null) { "ERROR - The error job is null" | Out-File $log -Encoding ASCII -Append Write-Host "ERROR - The error job is null" } if($outputjob -eq $null) { "ERROR - The output job is null" | Out-File $log -Encoding ASCII -Append Write-Host "ERROR - The output job is null" } $process.Start() $process.BeginErrorReadLine() if($process.StartInfo.RedirectStandardOutput) { $process.BeginOutputReadLine() } $ret = $null if($timeout -eq 0) { $process.WaitForExit() $ret = $true } else { if(-not($process.WaitForExit($timeout))) { Write-Host "ERROR - The process is not completed, after the specified timeout: $($timeout)" $ret = $false } else { $ret = $true } } # Cancel the event registrations Remove-Event * -ErrorAction SilentlyContinue Unregister-Event -SourceIdentifier Common.LaunchProcess.Error Unregister-Event -SourceIdentifier Common.LaunchProcess.Output Stop-Job $errorjob.Id Remove-Job $errorjob.Id Stop-Job $outputjob.Id Remove-Job $outputjob.Id $ret}$repo = <your repo>$process = Create-Process$process.StartInfo.RedirectStandardOutput = $true$process.StartInfo.FileName = "git.exe"$process.StartInfo.Arguments = "clone $($repo)"$process.StartInfo.WorkingDirectory = $dirLaunch-Process $process $global:logTerminate-Process $process
The log file must be in the global scope because the routine which runs the event processing is not in the script scope.
Sample of my log file:
Out - Cloning into ''...ERROR - Checking out files: 22% (666/2971)
ERROR - Checking out files: 23% (684/2971)
ERROR - Checking out files: 24% (714/2971)
You can do this by putting the git clone command inside an advanced function e.g.:
function Clone-Git { [CmdletBinding()] param($repoUrl, $localRepoDir) git clone $repoUrl $localRepoDir}Clone-Git $RepositoryUrl $localRepoDirectory -ev cloneErrors$cloneErrors
If you use System.Diagnostics.Process
to start Git, you can redirect all the error and output.
I just had to solve this problem for Inkscape:
$si = New-Object System.Diagnostics.ProcessStartInfo$si.Arguments = YOUR PROCESS ARGS$si.UseShellExecute = $false$si.RedirectStandardOutput = $true$si.RedirectStandardError = $true$si.WorkingDirectory = $workingDir$si.FileName = EXECUTABLE LOCATION$process = [Diagnostics.Process]::Start($si)while (!($process.HasExited)){ // Do what you want with strerr and stdout Start-Sleep -s 1 // Sleep for 1 second}
You can, of course, wrap this in a function with proper arguments...