The stderr stream
Basic case
When ErrorActionPreference
is Continue
, stderr in PowerShell
behaves similarly to stderr in other shells. Most stderr output by
commands you call will be sent directly to the PowerShell process's own
stderr. It is interesting to note, however, that assigning the stderr
command to a variable, casting it to Void
, or piping it to Out-Null
produces different results.
Here is the output from writing directly to stderr. All versions print both lines to stderr.
$local:ErrorActionPreference = 'Continue'
cmd /c 'echo printing to stderr 1 >&2'
cmd /c 'echo printing to stderr 2 >&2'
printing to stderr 1 printing to stderr 2
Here we are redirecting to stdout, $null
, and a file. Nothing prints
the $null
line. Versions 2 and 5 write the stdout line to stderr,
while PowerShell Core writes it to stdout. All versions correctly write
to the file, with versions 2 and 5 including stack traces.
$local:ErrorActionPreference = 'Continue'
cmd /c 'echo redirecting stderr to stdout >&2' 2>&1
cmd /c 'echo redirecting stderr to $null >&2' 2> $null
cmd /c 'echo redirecting stderr to a file >&2' 2> error.txt
Write-Output "error.txt: $((Get-Content error.txt) -join `"`n`")"
error.txt: cmd : redirecting stderr to a file At C:\Users\runneradmin\AppData\Local\Temp\pwsh-live-doc_848fb1b4-1b82-4ef2-9a73-5fc88ca29dab\__script.ps1:6 char:16 + cmd <<<< /c 'echo redirecting stderr to a file >&2' 2> error.txt + CategoryInfo : NotSpecified: (redirecting stderr to a file :String) [], RemoteException + FullyQualifiedErrorId : NativeCommandError
error.txt: cmd : redirecting stderr to a file At C:\Users\runneradmin\AppData\Local\Temp\pwsh-live-doc_0b57880c-c06e-4211-b386-da8eba3ec982\__script.ps1:6 char:13 + cmd /c 'echo redirecting stderr to a file >&2' 2> error.t ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (redirecting stderr to a file :String) [], RemoteException + FullyQualifiedErrorId : NativeCommandError
redirecting stderr to stdout error.txt: redirecting stderr to a file
cmd : redirecting stderr to stdout At C:\Users\runneradmin\AppData\Local\Temp\pwsh-live-doc_848fb1b4-1b82-4ef2-9a73-5fc88ca29dab\__script.ps1:4 char:16 + cmd <<<< /c 'echo redirecting stderr to stdout >&2' 2>&1 + CategoryInfo : NotSpecified: (redirecting stderr to stdout :String) [], RemoteException + FullyQualifiedErrorId : NativeCommandError
cmd : redirecting stderr to stdout At C:\Users\runneradmin\AppData\Local\Temp\pwsh-live-doc_0b57880c-c06e-4211-b386-da8eba3ec982\__script.ps1:4 char:13 + cmd /c 'echo redirecting stderr to stdout >&2' 2>&1 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (redirecting stderr to stdout :String) [], RemoteException + FullyQualifiedErrorId : NativeCommandError
And finally here we redirect to a variable, cast to Void
, and
redirect to Out-Null
. All PowerShell versions behave the same for
these examples.
All versions assign stderr to the variable if it's redirected and
otherwise print to stderr and leave the variable blank. The
non-redirected Void
and Out-Null
cases print to stderr, and the
redirected cases print nothing.
$local:ErrorActionPreference = 'Continue'
$v = cmd /c 'echo assigning command result to a variable >&2'
Write-Output "`$v: $v"
$v = cmd /c 'echo assigning redirected command result to a variable >&2' 2>&1
Write-Output "`$v: $v"
[Void] (cmd /c 'echo casting stderr command to Void >&2')
[Void] (cmd /c 'echo casting redirected stderr command to Void >&2' 2>&1)
cmd /c 'echo piping stderr command to Out-Null >&2' | Out-Null
cmd /c 'echo piping redirected stderr command to Out-Null >&2' 2>&1 | Out-Null
$v: $v: assigning redirected command result to a variable
assigning command result to a variable casting stderr command to Void piping stderr command to Out-Null
If you redirect stderr while ErrorActionPreference
is Stop
, an
exception is generated. Here we have the same groupings, first writing
directly to stderr. Since we aren't redirecting stderr, no exceptions
are thrown.
$local:ErrorActionPreference = 'Stop'
try {
cmd /c 'echo printing to stderr 1 >&2'
} catch {
Write-Output "Caught: $_"
}
try {
cmd /c 'echo printing to stderr 2 >&2'
} catch {
Write-Output "Caught: $_"
}
printing to stderr 1 printing to stderr 2
Then redirecting to stdout, $null
, or a file all cause exceptions.
$local:ErrorActionPreference = 'Stop'
try {
cmd /c 'echo redirecting stderr to stdout >&2' 2>&1
} catch {
Write-Output "Caught: $_"
}
try {
cmd /c 'echo redirecting stderr to $null >&2' 2> $null
} catch {
Write-Output "Caught: $_"
}
try {
cmd /c 'echo redirecting stderr to a file >&2' 2> error.txt
} catch {
Write-Output "Caught: $_"
}
Write-Output "error.txt: $((Get-Content error.txt) -join `"`n`")"
Caught: redirecting stderr to stdout Caught: redirecting stderr to $null Caught: redirecting stderr to a file error.txt:
And finally assigning to a variable, casting to Void
, or redirecting
to Out-Null
throw exceptions for all cases where stderr is redirected
to stdout (which makes sense, since redirecting to stdout by itself
throws).
Assigning to a variable without redirection leaves the variable empty
and prints to stderr. Casting stderr to Void
and piping to Out-Null
both print to stderr as well.
$local:ErrorActionPreference = 'Stop'
try {
$v = cmd /c 'echo assigning command result to a variable >&2'
Write-Output "`$v: $v"
} catch {
Write-Output "Caught: $_"
}
try {
$v = cmd /c 'echo assigning redirected command result to a variable >&2' 2>&1
Write-Output "`$v: $v"
} catch {
Write-Output "Caught: $_"
}
try {
[Void] (cmd /c 'echo casting stderr command to Void >&2')
} catch {
Write-Output "Caught: $_"
}
try {
[Void] (cmd /c 'echo casting redirected stderr command to Void >&2' 2>&1)
} catch {
Write-Output "Caught: $_"
}
try {
cmd /c 'echo piping stderr command to Out-Null >&2' | Out-Null
} catch {
Write-Output "Caught: $_"
}
try {
cmd /c 'echo piping redirected stderr command to Out-Null >&2' 2>&1 | Out-Null
} catch {
Write-Output "Caught: $_"
}
$v: Caught: assigning redirected command result to a variable Caught: casting redirected stderr command to Void Caught: piping redirected stderr command to Out-Null
assigning command result to a variable casting stderr command to Void piping stderr command to Out-Null
Class methods
This behavior has interesting consequences for class methods (See
Method stdio). Since non-void methods automatically suppress
stdio, it's as if they were redirecting stderr to $null
, so printing
to stderr within a non-void method will produce an exception when
ErrorActionPreference
is Stop
.
Void
methods don't suppress stderr, even though they suppress stdout.
So, calling VoidFunc
will print to stderr.
$local:ErrorActionPreference = 'Stop'
class C {
[String] StringFunc() {
cmd /c 'echo StringFunc: printing to stderr 1 >&2'
cmd /c 'echo StringFunc: printing to stderr 2 >&2'
return 'some string'
}
[Void] VoidFunc() {
cmd /c 'echo VoidFunc: printing to stderr 1 >&2'
cmd /c 'echo VoidFunc: printing to stderr 2 >&2'
}
}
$c = [C]::new()
try {
$c.StringFunc()
} catch {
Write-Output "Caught: $_"
}
try {
$c.VoidFunc()
} catch {
Write-Output "Caught: $_"
}
Caught: StringFunc: printing to stderr 1
VoidFunc: printing to stderr 1 VoidFunc: printing to stderr 2
When ErrorActionPreference
is Continue
, StringFunc
's return value
gets printed to stdout (which makes sense, since it's returned), and it
prints its other lines to stderr. VoidFunc
prints both its lines to
stderr.
What's interesting is that in GitHub Actions runners (where this site
was generated), everything in StringFunc
is printed to stderr without
a newline after it. This results in the first line from VoidFunc
being on the same line as all StringFunc
output. On my (binyomen's)
machine, nothing in StringFunc
is printed at all. Since methods with
return values should supress stdio, it seems like my machine has the
"correct" behavior. This is, needless to say, extremely confusing, and
is tracked by Issue
29.
$local:ErrorActionPreference = 'Continue'
class C {
[String] StringFunc() {
cmd /c 'echo Note: These lines only seem to print on GitHub Actions >&2'
cmd /c 'echo StringFunc: printing to stderr 1 >&2'
cmd /c 'echo StringFunc: printing to stderr 2 >&2'
return 'some string'
}
[Void] VoidFunc() {
cmd /c 'echo VoidFunc: printing to stderr 1 >&2'
cmd /c 'echo VoidFunc: printing to stderr 2 >&2'
}
}
$c = [C]::new()
try {
$c.StringFunc()
} catch {
Write-Output "Caught: $_"
}
try {
$c.VoidFunc()
} catch {
Write-Output "Caught: $_"
}
some string
Note: These lines only seem to print on GitHub Actions StringFunc: printing to stderr 1 StringFunc: printing to stderr 2 VoidFunc: printing to stderr 1 VoidFunc: printing to stderr 2
See also
This PowerShellTraps page is also an incredible source for information about stderr.