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

3

7

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 ?

Hymnal answered 18/6, 2015 at 4:15 Comment(0)
C
7

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"
Cardiovascular answered 18/6, 2015 at 11:28 Comment(0)
U
1

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"
Unclassified answered 18/6, 2015 at 5:25 Comment(0)
I
0

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

Intension answered 29/3, 2021 at 20:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.