PowerShell - redirect executable's stderr to file or variable but still have stdout go to console PowerShell - redirect executable's stderr to file or variable but still have stdout go to console powershell powershell

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...