How do I write to standard error in PowerShell?
Solution 1
Use Write-Error
to write to stderr. To redirect stderr to file use:
Write-Error "oops" 2> /temp/err.msg
or
exe_that_writes_to_stderr.exe bogus_arg 2> /temp/err.msg
Note that PowerShell writes errors as error records. If you want to avoid the verbose output of the error records, you could write out the error info yourself like so:
PS> Write-Error "oops" -ev ev 2>$null
PS> $ev[0].exception
oops
-EV
is short (an alias) for -ErrorVariable
. Any errors will be stored in the variable named by the argument to this parameter. PowerShell will still report the error to the console unless we redirect the error to $null
.
Solution 2
You probably want this:
$host.ui.WriteErrorLine('I work in any PowerShell host')
You might also see the following, but it assumes your PowerShell host is a console window/device, so I consider it less useful:
[Console]::Error.WriteLine('I will not work in the PowerShell ISE GUI')
Solution 3
Note:
-
This answer is about writing to stderr from the perspective of the outside world when a PowerShell script is called from there; while the answer is written from the perspective of the Windows shell,
cmd.exe
, it equally applies to Unix shells such asbash
when combined with PowerShell Core. -
By contrast, from within Powershell, you should use
Write-Error
, as explained in Keith Hill's answer. -
Sadly, there is no unified approach that will work from both within PowerShell and from the outside - see this answer of mine for a discussion.
To add to @Chris Sear's great answer:
While $host.ui.WriteErrorLine
should work in all hosts, it doesn't (by default) write to stderr when invoked via cmd.exe
, such as from a batch file.
[Console]::Error.WriteLine
, by contrast, always does.
So if you want to write a PowerShell script that plays nicely in terms of output streams when invoked from cmd.exe
, use the following function, Write-StdErr
, which uses [Console]::Error.WriteLine
in the regular PS / cmd.exe
host (console window), and $host.ui.WriteErrorLine
otherwise:
<#
.SYNOPSIS
Writes text to stderr when running in a regular console window,
to the host''s error stream otherwise.
.DESCRIPTION
Writing to true stderr allows you to write a well-behaved CLI
as a PS script that can be invoked from a batch file, for instance.
Note that PS by default sends ALL its streams to *stdout* when invoked from
cmd.exe.
This function acts similarly to Write-Host in that it simply calls
.ToString() on its input; to get the default output format, invoke
it via a pipeline and precede with Out-String.
#>
function Write-StdErr {
param ([PSObject] $InputObject)
$outFunc = if ($Host.Name -eq 'ConsoleHost') {
[Console]::Error.WriteLine
} else {
$host.ui.WriteErrorLine
}
if ($InputObject) {
[void] $outFunc.Invoke($InputObject.ToString())
} else {
[string[]] $lines = @()
$Input | % { $lines += $_.ToString() }
[void] $outFunc.Invoke($lines -join "`r`n")
}
}
Optional background information: How the PowerShell CLI's output streams are seen by outside callers:
Internally, PowerShell has more than the traditional output streams (stdout and stderr), and their count has increased over time (try Write-Warning "I'll go unheard." 3> $null
as an example, and read more at Get-Help about_Redirection
.
When interfacing with the outside world, PowerShell must map the non-traditional output streams to stdout and stderr.
Strangely, however, PowerShell by default sends all its streams (including Write-Host
and $host.ui.WriteErrorLine()
output) to stdout when invoked from cmd.exe
, even though mapping PowerShell's error stream to stderr would be the logical choice. This behavior has been in effect since (at least) v2 and still applies as of v5.1 (and probably won't change for reasons of backward compatibility - see GitHub issue #7989).
You can verify this with the following command, if you invoke it from cmd.exe
:
powershell -noprofile -command "'out'; Write-Error 'err'; Write-Warning 'warn'; Write-Verbose -Verbose 'verbose'; $DebugPreference='Continue'; write-debug 'debug'; $InformationPreference='Continue'; Write-Information 'info'; Write-Host 'host'; $host.ui.WriteErrorLine('uierr'); [Console]::Error.WriteLine('cerr')" >NUL
The command writes to all PowerShell output streams (when you run on a pre-PowerShell-v5 version, you'll see an additional error message relating to Write-Information
, which was introduced in PowerShell v5) and has cmd.exe
redirect stdout only to NUL
(i.e., suppress stdout output; >NUL
).
You will see no output except cerr
(from [Console]::Error.WriteLine()
, which writes directly to stderr) - all of PowerShell's streams were sent to stdout.
Perhaps even more strangely, it is possible to capture PowerShell's error stream, but only with a redirection:
If you change >NUL
to 2>NUL
above, it is exclusively PowerShell's error stream and $host.ui.WriteErrorLine()
output that will be suppressed; of course, as with any redirection, you can alternatively send it to a file.
(As stated, [Console]::Error.WriteLine()]
always outputs to stderr, whether the latter is redirected or not.)
To give a more focused example (again, run from cmd.exe
):
powershell -noprofile -command "'out'; Write-Error 'err'" 2>NUL
The above only outputs out
- Write-Error
's output is suppressed.
To summarize:
-
Without any (
cmd.exe
) redirection or with only a stdout redirection (>...
or1>...
), PowerShell sends all its output streams to stdout. -
With a stderr redirection (
2>...
), PowerShell selectively sends its error stream to stderr (irrespective of whether stdout is also redirected or not). -
As a corollary, the following common idiom does not work as expected:
powershell ... >data-output.txt
This will not, as one might expect, send only stdout to filedata-output.txt
while printing stderr output to the terminal; instead, you'd have to usepowershell ... >data-output.txt 2>err-output.tmp; type err-output.tmp >&2; del err-output.tmp
It follows that PowerShell is aware of cmd.exe
's redirections and adjusts its behavior intentionally.
(This is also evident from PowerShell producing colored output in the cmd.exe
console while stripping the color codes when output is redirected to a file.)
Brett Ryan
I am an application developer with a responsibilities in Java/J2EE web, C#, Linux, jQuery, Objective-C, Progress Software (OpenEdge). I work mostly with rich clients, the NetBeans Platform and HTML environments.
Updated on July 20, 2022Comments
-
Brett Ryan almost 2 years
I'm having trouble figuring out how to both echo to the standard error stream and redirect the error stream of an executable.
I have come from a Bourne shell and Korn shell background, of which I would use;
# Write to stderr echo "Error Message!" >&2 # Redirect stderr to file /do/error 2>/tmp/err.msg
-
Brett Ryan about 13 yearsUnfortunately this isn't what I'm after, I don't want the stack-trace appearing, I just want to write to the error stream with a single error message.
-
Keith Hill about 13 yearsKeep in mind that PowerShell is object-oriented and usually pushes objects around instead of text.
Write-Error
is how you write to the error stream.echo
is just a compatibility alias forWrite-Object
which is how your place stuff on the output (stdout) stream. -
Brett Ryan about 13 yearsIn your updated version it still doesn't help, I want to be able to write a piece of text only to stderr, so that then I can have two redirects
cmd >stdout.log 2>stderr.log
. Consider the ability totype
content and pipe to stderr, currently I believe it can't be done by the looks of things. -
Ohad Schneider about 8 yearsIt seems
Write-Error "oops" -ev ev 2>&1 > $null
does not redirect anything tostderr
.Write-Error "oops"
works great but you're stuck with the verbose output. -
Ohad Schneider about 8 years
WriteErrorLine
is a bad idea. Look at the docs (msdn.microsoft.com/en-us/library/…): This method writes a line to the error display of the host. In other words, this is a UI method, that sometimes works as you expect and redirects to stderr, and sometimes (most times?) doesn't (see mklement0's answer: stackoverflow.com/a/15669365/67824). As Keith Hill says,Write-Error
is the best option. -
Keith Hill about 8 years@OhadSchneider I'm not sure what you mean here. That command was meant to demonstrate how to "avoid" the stderr output from being displayed. If you need that Error (stderr) info (but don't want it displayed), it is available in the
$ev
variable. -
Ohad Schneider about 8 yearsWhat I mean is that no only do you avoid the
stderr
output from being displayed, butoops
is not actually redirected to stderr at all (I tried executing it externally andoops
came out only in stdout). -
Steven Vachon about 8 years"oops" is still being outputted to StdErr for me, despite being sent to StdOut and
$null
. -
Keith Hill about 8 years@OhadSchneider You mentioned "externally". Are you trying to read PowerShell.exe's stderr output from a console exe? If so, that is a different beast.
-
Keith Hill about 8 years@StevenVachon What do you mean exactly by "outputted to StdErr"? Yes, the error record will be preserved in $Error but the message is not put in the Error stream. You can see this like so
$ps = [PowerShell]::Create(); $null = $ps.AddScript('Write-Error Oops 2>&1 > $null'); $ps.Invoke(); $ps.Streams.Error
. That shows there is nothing in the Error stream. Now try that again but remove the2>&1 > $null
part and you will see the error in the Error stream. -
Ohad Schneider about 8 yearsI'm confused. Why would you ever want
Write-Error "oops" -ev ev 2>&1 > $null
if we all agree nothing is redirected to stderr? Besides, the OP was clear in what he wanted to do - he wanted the equivalent ofecho "Error Message!" >&2
(namely write to the error stream) in PowerShell.Write-Error
comes close (the only issue being you can't control the verbosity), but when you redirect it to $null you lose even that. -
Keith Hill about 8 years@OhadSchneider The last part of the answer was simply an example of how you could control the verbosity of output resulting from an error record going into the Error stream. You don't lose the error record when you redirect it to $null if you use
-ev ev
. It stores the error record in the variable$ev
. Plus it would also be available in the$error
global. If necessary, you can access the message of the error text later with$ev.Exception.Message
. I did update the answer to simplify the example to justWrite-Error oops -ev ev 2>$null
. -
Ohad Schneider about 8 yearsOK, but I still don't understand why you would ever do
Write-Error "oops" -ev ev 2>$null; $ev[0].exception
if the end result, as far as streams go, is the same asecho oops
. -
Keith Hill about 8 years@OhadSchneider That was more of a pedagogical example and not practical. A practical example would be
Get-ChildItem C:\Windows -Recurse *.dll -ev ev 2>$null
. Get's the "red" out but you can access$ev
for all the non-terminating errors generated by the command. -
Ohad Schneider about 8 yearsAh, I see now. So the second example addressed the second part of the OP's question, about redirecting the error stream (not writing to it) - in this case directly to a variable to avoid the verbose error record.
-
Keilaron over 4 yearsThe trouble with
Write-Error
, however, is that while the documentation claims this declares a "non-terminating error" it will terminate your PowerShell script if you've changed$ErrorActionPreference
to "Stop".Write-Warning
may be a better option but it uses a different stream as indicated by mklement0's excellent answer. -
Marc over 3 yearsI am seeing PS7 send
$Host.UI.WriteErrorLine
output to stdout and not stderr even when called from within the pwsh shell. -
mklement0 over 3 years@Marc: Inside PowerShell, there is no such thing as stdout and stderr, only PowerShell's 6 streams vs. to-host output. Outside PowerShell,
$Host.UI.WriteErrorLine
- as doesWrite-Error
- writes to stdout by default, but you can redirect it with2>
, as explained in the answer. To write to the outside world's stderr by default, use[Console]::Error.WriteLine()
, as recommended in the answer, at the expense of being able to capture such output inside a PowerShell session. -
Marc over 3 yearsI feel I'm swimming against the stream: how does PowerShell intend developers to capture error output between scripts? I want to call a script.ps1 and capture an error stream/object. Should I just throw? Setting
-ErrorVariable $foo
seems to have no effect on$foo
regardless of any errors thrown or written, -
mklement0 over 3 years@Marc: You can only use
-ErrorVariable
if the command is an advanced script or function, or a compiled cmdlet, and then you must use-ErrorVariable foo
- that is, you mustn't use the$
sigil. -
Marc over 3 yearsOK, but it's really weird that
./scriptlet.ps1 2>
doesn't capture errors whilerun-cmdlet 2>
does. -
mklement0 over 3 years@Marc: I don't understand the distinction. Inside PowerShell, anything that is written to the error stream can be redirected with
2>
, irrespective of whether it is a simple or advanced function/script or a cmdlet or even an external program writing to stderr. PowerShell commands write to the error stream either explicitly withWrite-Error
or implicitly by triggering non-terminating or statement-terminating errors.