Redirect two or more Powershell streams other than output stream to the same file

8.1k views Asked by At

In Powershell, we can combine the standard output stream with any other stream and then redirect (write) the result to the same file.

Examples:

Powershell -File "C:\myscript.ps1" 2>&1> "C:\log.txt"  
Powershell -File "C:\myscript.ps1" 3>&2>&1> "C:\log.txt"

Suppose I use Write-Error and Write-Warning statements in myscript.ps1 and I want to write only the errors and the warnings to the same file.

Note: if I am not mistaken, 1 is the output stream, 2 is the error stream and 3 the warning stream.

My first logical try was then to use 3>&2> - if combining 1 and 2 works, why would not 3 and 2 ? See below:

Powershell -File "C:\myscript.ps1" 3>&2> "C:\log.txt"

However, 3>&2> does not work as a valid redirect operator.

I could get around trying:

Powershell -File "C:\myscript.ps1" 3>"C:\warninglog.txt" 2>"C:\errorlog.txt"
but I really want to write to the same file.

If I try to run:

Powershell -File "C:\myscript.ps1" 3>"C:\log.txt" 2>"C:\log.txt" 

it seems the error stream (2) never gets written to log.txt because file is locked by the warning stream.

Is there a way to combine two (or more) output streams into a single stream and redirect the result to the same file ?

3

There are 3 answers

0
Lieven Keersmaekers On

You can redirect the error and warning stream to the output stream separately so instead of doing this

  3>&2>&1

you should be doing this

  3>&1 2>&1

Adapted from Understanding streams

function Write-Messages
{
    Write-Host "Host message"
    Write-Output "Output message"
    Write-Verbose "Verbose message"
    Write-Warning "Warning message"
    Write-Error "Error message"
    Write-Debug "Debug message"
}

$DebugPreference = "Continue"
$VerbosePreference = "Continue"

Write-Messages 3>&1 2>&1 > $env:TEMP\test.txt

Applied to your example, this would become

Powershell -File "C:\myscript.ps1" 3>&1 2>&1> "C:\log.txt"

or you can redirect all streams to your file

Powershell -File "C:\myscript.ps1" *> "C:\log.txt"
0
Ansgar Wiechers On

The verbose, warning, and debug streams are merged into STDOUT when you run PowerShell scripts via

powershell -File "C:\myscript.ps1"

so you can't redirect them separately anymore. Only the error stream is different, since it seems to go to both STDOUT and STDERR, where it can be redirected by 1> as well as 2>.

Demonstration:

C:\>type test.ps1
$DebugPreference = "Continue"
$VerbosePreference = "Continue"
Write-Output "Output message"
Write-Error "Error message"
Write-Verbose "Verbose message"
Write-Warning "Warning message"
Write-Debug "Debug message"

C:\>powershell -File .\test.ps1
Output message
C:\test.ps1 : Error message
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,test.ps1

VERBOSE: Verbose message
WARNING: Warning message
DEBUG: Debug message

C:\>powershell -File .\test.ps1 2>nul 3>nul 4>nul 5>nul
Output message
VERBOSE: Verbose message
WARNING: Warning message
DEBUG: Debug message

C:\>powershell -File .\test.ps1 1>nul

C:\>_

If you want to redirect the verbose, warning, or debug stream separately you must use -Command instead of -File and do the redirection within PowerShell:

C:\>powershell -Command ".\test.ps1 2>$null 3>$null 5>$null"
Output message
VERBOSE: Verbose message

However, while in CMD you can redirect any handle to any other handle (3>&2, 1>&5, ...), PowerShell redirection only supports redirection to either a file (3>C:\out.txt) or the success output stream (3>&1). Trying to redirect to any other stream will throw an error:

C:\>powershell -Command ".\test.ps1 2>out.txt 3>&2"
At line:1 char:22
+ .\test.ps1 2>out.txt 3>&2
+                      ~~~~
The '3>&2' operator is reserved for future use.
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : RedirectionNotSupported

as will redirecting different streams to the same file:

C:\>powershell -Command ".\test.ps1 2>out.txt 3>>out.txt"
out-file : The process cannot access the file 'C:\out.txt' because it is being
used by another process.
At line:1 char:1
+ .\test.ps1 2>out.txt 3>>out.txt
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OpenError: (:) [Out-File], IOException
    + FullyQualifiedErrorId : FileOpenFailure,Microsoft.PowerShell.Commands.OutFileCommand

If merging warning and success output is an option for you, you could do something like this:

powershell -Command ".\test.ps1 >out.txt 3>&1 2>error.log"

or like this:

powershell -Command ".\test.ps1 >out.txt 3>&1 2>&1"

or (redirecting all streams) like this:

powershell -Command ".\test.ps1 *>out.txt"

Otherwise the only option I see is to redirect to different files:

powershell -Command ".\test.ps1 3>warning.log 2>error.log"
0
Jim On

If you want to say capture the output stream into a variable and capture other streams into a different variable then you can do this using sub expressions:

$errorOutput = $( $output = & $command $arguments ) 2>&1

This is from an excellent article by Simon Wahlin