powershell

Redirect warning and error stream to the same file


I try to redirect warning and error stream to one file, and verbose and debug stream to another one, but PowerShell does not let me do so.

When I try to redirect using the & syntax, I get this error:

.\test-streams.ps1 -Debug -Verbose 1> test-streams.log 2> test-streams.err 3>&2
At line:1 char:76
+ ... eams.ps1 -Debug -Verbose 1> test-streams.log 2> test-streams.err 3>&2
+                                                                      ~~~~
The '3>&2' operator is reserved for future use.
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException

and when I try to give the same file I get this error:

.\test-streams.ps1 -Debug -Verbose 1> test-streams.log 2> test-streams.err 3> test-streams.err
out-file : The process cannot access the file 'test-streams.err' because it is being used by another process.
At line:1 char:1
+ .\test-streams.ps1 -Debug -Verbose 1> test-streams.log 2> test-stream ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OpenError: (:) [Out-File], IOException
    + FullyQualifiedErrorId : FileOpenFailure,Microsoft.PowerShell.Commands.OutFileCommand````
    + FullyQualifiedErrorId : RedirectionNotSupported

The same errors appear when I try with 4> file.txt 5>&4 or 4> file.txt 5> file.txt

The only combination which does not raise an error is >&1 but all goes into the very same file and that's not what I'm looking for.

Did I miss something? Or is it impossible?

Context:

PSVersion                      5.1.14393.8244
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.14393.8244
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

Solution

  • Unfortunately you can only redirect streams to Success (&1) or to a file, this is described in the about_Redirection documentation. You could workaround this with some work, below example redirects all streams to success (*>&1) and sends them to the pipeline where you can determine, using a hash table in this case, if the incoming input should be redirected to a file or not.

    This approach isn't the most efficient one because we're appending to a file (-Append), this means that for every input object that should be redirected, the destination file stream is being opened, appended to and closed. There are also ways to workaround this by using for example a StreamWriter or a Steppable Pipeline for each file however the answer would increase in complexity.

    # a scriptblock to simulate a ps1 file
    $testScript = {
        Write-Warning 'warn'
        Write-Error 'err'
        Write-Debug 'dbg' -Debug
        Write-Verbose 'verb' -Verbose
        'success output'
    }
    
    # Key: input type / Value: destination file path
    $redirectiontable = @{
        [System.Management.Automation.WarningRecord] = 'warnAndErr.txt'
        [System.Management.Automation.ErrorRecord]   = 'warnAndErr.txt'
        [System.Management.Automation.DebugRecord]   = 'dbgAndVerb.txt'
        [System.Management.Automation.VerboseRecord] = 'dbgAndVerb.txt'
    }
    
    # redirect all streams to success and send to pipeline
    & $testScript *>&1 | ForEach-Object {
        # if the input type exists in the redirection table, get the path
        if ($path = $redirectiontable[$_.GetType()]) {
            # append to corresponding file
            $_ | Out-File $path -Append
            # and go to next iteration
            return
        }
    
        # else, send to pipeline
        $_
    }