I want to have access to the same message that Powershell prints when you send an error record to the output stream
Example:
This is the exception message At
C:\Documents and
Settings\BillBillington\Desktop\psTest\exThrower.ps1:1
char:6
- throw <<<< (New-Object ArgumentException(«This is the
exception»));
- CategoryInfo : OperationStopped: (:) [],
ArgumentException- FullyQualifiedErrorId : This is the exception
I when a get the last ErrorRecord by doing $Error[0] I can’t seem to figure out how to get this information in a simple way
I found this ‘Resolve-Error’ function from the community extensions here which does roughly what I want but it prints a huge semi-formatted list of stuff I don’t need that I have to then strip
Is there way of accessing the message that Powershell uses or failing that a simpler way of getting hash of the values I care about so I can put them into a string in a format of my choosing?
Glorfindel
22k13 gold badges81 silver badges109 bronze badges
asked Aug 4, 2010 at 10:33
How about:
$x = ($error[0] | out-string)
Is that what you wanted?
answered Aug 4, 2010 at 12:47
tomasrtomasr
13.7k3 gold badges39 silver badges30 bronze badges
0
If you want a bit shorter message (more user friendly sometimes?) than @tomasr suggests this will do:
$error[0].ToString() + $error[0].InvocationInfo.PositionMessage
You will get something like:
Cannot find path 'C:\TEMP\_100804_135716\missing' because it does not exist.
At C:\TEMP\_100804_135716\test.ps1:5 char:15
+ Get-ChildItem <<<< missing
This technical info will be excluded:
+ CategoryInfo : ObjectNotFound: (C:\TEMP\_100804_135716\missing:String) [Get-ChildItem], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
answered Aug 4, 2010 at 13:13
Roman KuzminRoman Kuzmin
40.7k11 gold badges96 silver badges117 bronze badges
I took it a bit further because I didn’t like the multilines from $error[0].InvocationInfo.PositionMessage.
Function FriendlyErrorString ($thisError) {
[string] $Return = $thisError.Exception
$Return += "`r`n"
$Return += "At line:" + $thisError.InvocationInfo.ScriptLineNumber
$Return += " char:" + $thisError.InvocationInfo.OffsetInLine
$Return += " For: " + $thisError.InvocationInfo.Line
Return $Return
}
[string] $ErrorString = FriendlyErrorString $Error[0]
$ErrorString
You can look at what else is availible to construct your own String via:
$Error | Get-Member
$Error[0].InvocationInfo | Get-Member
answered Jun 18, 2013 at 16:58
KentKent
2813 silver badges3 bronze badges
Foreach ($Errors in $Error){
#Log Eintrag wird zusammengesetzt und in errorlog.txt geschrieben
"[$Date] $($Errors.CategoryInfo.Category) $($Errors.CategoryInfo.Activity) $($Errors.CategoryInfo.Reason) $($Errors.CategoryInfo.TargetName) $($Errors.CategoryInfo.TargetType) $($Errors.Exception.Message)" |Add-Content $Path\errorlog.txt -Encoding UTF8
}
You can also do this and you will get all Informations about the Error
answered Mar 17, 2017 at 13:17
Similar to @tomasr, but shorter:
$($error[0])
For all errors in a script:
$($error)
answered Dec 15, 2020 at 10:40
MinkusMinkus
3552 silver badges13 bronze badges
В Powershell существует несколько уровней ошибок и несколько способов их обработать. Проблемы одного уровня (Non-Terminating Errors) можно решить с помощью привычных для Powershell команд. Другой уровень ошибок (Terminating Errors) решается с помощью исключений (Exceptions) стандартного, для большинства языков, блока в виде Try, Catch и Finally.
Как Powershell обрабатывает ошибки
До рассмотрения основных методов посмотрим на теоретическую часть.
Автоматические переменные $Error
В Powershell существует множество переменных, которые создаются автоматически. Одна из таких переменных — $Error хранит в себе все ошибки за текущий сеанс PS. Например так я выведу количество ошибок и их сообщение за весь сеанс:
Get-TestTest
$Error
$Error.Count
При отсутствии каких либо ошибок мы бы получили пустой ответ, а счетчик будет равняться 0:
Переменная $Error являет массивом и мы можем по нему пройтись или обратиться по индексу что бы найти нужную ошибку:
$Error[0]
foreach ($item in $Error){$item}
Свойства объекта $Error
Так же как и все что создается в Powershell переменная $Error так же имеет свойства (дополнительную информацию) и методы. Названия свойств и методов можно увидеть через команду Get-Member:
$Error | Get-Member
Например, с помощью свойства InvocationInfo, мы можем вывести более структурный отчет об ошибки:
$Error[0].InvocationInfo
Методы объекта $Error
Например мы можем очистить логи ошибок используя clear:
$Error.clear()
Критические ошибки (Terminating Errors)
Критические (завершающие) ошибки останавливают работу скрипта. Например это может быть ошибка в названии командлета или параметра. В следующем примере команда должна была бы вернуть процессы «svchost» дважды, но из-за использования несуществующего параметра ‘—Error’ не выполнится вообще:
'svchost','svchost' | % {Get-Process -Name $PSItem} --Error
Не критические ошибки (Non-Terminating Errors)
Не критические (не завершающие) ошибки не остановят работу скрипта полностью, но могут вывести сообщение об этом. Это могут быть ошибки не в самих командлетах Powershell, а в значениях, которые вы используете. На предыдущем примере мы можем допустить опечатку в названии процессов, но команда все равно продолжит работу:
'svchost111','svchost' | % {Get-Process -Name $PSItem}
Как видно у нас появилась информация о проблеме с первым процессом ‘svchost111’, так как его не существует. Обычный процесс ‘svchost’ он у нас вывелся корректно.
Параметр ErrorVariable
Если вы не хотите использовать автоматическую переменную $Error, то сможете определять свою переменную индивидуально для каждой команды. Эта переменная определяется в параметре ErrorVariable:
'svchost111','svchost' | % {Get-Process -Name $PSItem } -ErrorVariable my_err_var
$my_err_var
Переменная будет иметь те же свойства, что и автоматическая:
$my_err_var.InvocationInfo
Обработка некритических ошибок
У нас есть два способа определения последующих действий при ‘Non-Terminating Errors’. Это правило можно задать локально и глобально (в рамках сессии). Мы сможем полностью остановить работу скрипта или вообще отменить вывод ошибок.
Приоритет ошибок с $ErrorActionPreference
Еще одна встроенная переменная в Powershell $ErrorActionPreference глобально определяет что должно случится, если у нас появится обычная ошибка. По умолчанию это значение равно ‘Continue’, что значит «вывести информацию об ошибке и продолжить работу»:
$ErrorActionPreference
Если мы поменяем значение этой переменной на ‘Stop’, то поведение скриптов и команд будет аналогично критичным ошибкам. Вы можете убедиться в этом на прошлом скрипте с неверным именем процесса:
$ErrorActionPreference = 'Stop'
'svchost111','svchost' | % {Get-Process -Name $PSItem}
Т.е. скрипт был остановлен в самом начале. Значение переменной будет храниться до момента завершения сессии Powershell. При перезагрузке компьютера, например, вернется значение по умолчанию.
Ниже значение, которые мы можем установить в переменной $ErrorActionPreference:
- Continue — вывод ошибки и продолжение работы;
- Inquire — приостановит работу скрипта и спросит о дальнейших действиях;
- SilentlyContinue — скрипт продолжит свою работу без вывода ошибок;
- Stop — остановка скрипта при первой ошибке.
Самый частый параметр, который мне приходится использовать — SilentlyContinue:
$ErrorActionPreference = 'SilentlyContinue'
'svchost111','svchost' | % {Get-Process -Name $PSItem}
Использование параметра ErrorAction
Переменная $ErrorActionPreference указывает глобальный приоритет, но мы можем определить такую логику в рамках команды с параметром ErrorAction. Этот параметр имеет больший приоритет чем $ErrorActionPreference. В следующем примере, глобальная переменная определяет полную остановку скрипта, а в параметр ErrorAction говорит «не выводить ошибок и продолжить работу»:
$ErrorActionPreference = 'Stop'
'svchost111','svchost' | % {Get-Process -Name $PSItem -ErrorAction 'SilentlyContinue'}
Кроме ‘SilentlyContinue’ мы можем указывать те же параметры, что и в переменной $ErrorActionPreference.
Значение Stop, в обоих случаях, делает ошибку критической.
Обработка критических ошибок и исключений с Try, Catch и Finally
Когда мы ожидаем получить какую-то ошибку и добавить логику нужно использовать Try и Catch. Например, если в вариантах выше мы определяли нужно ли нам отображать ошибку или останавливать скрипт, то теперь сможем изменить выполнение скрипта или команды вообще. Блок Try и Catch работает только с критическими ошибками и в случаях если $ErrorActionPreference или ErrorAction имеют значение Stop.
Например, если с помощью Powershell мы пытаемся подключиться к множеству компьютеров один из них может быть выключен — это приведет к ошибке. Так как эту ситуацию мы можем предвидеть, то мы можем обработать ее. Процесс обработки ошибок называется исключением (Exception).
Синтаксис и логика работы команды следующая:
try {
# Пытаемся подключиться к компьютеру
}
catch [Имя исключения 1],[Имя исключения 2]{
# Раз компьютер не доступен, сделать то-то
}
finally {
# Блок, который выполняется в любом случае последним
}
Блок try мониторит ошибки и если она произойдет, то она добавится в переменную $Error и скрипт перейдет к блоку Catch. Так как ошибки могут быть разные (нет доступа, нет сети, блокирует правило фаервола и т.д.) то мы можем прописывать один блок Try и несколько Catch:
try {
# Пытаемся подключится
}
catch ['Нет сети']['Блокирует фаервол']{
# Записываем в файл
}
catch ['Нет прав на подключение']{
# Подключаемся под другим пользователем
}
Сам блок finally — не обязательный и используется редко. Он выполняется самым последним, после try и catch и не имеет каких-то условий.
Catch для всех типов исключений
Как и было показано выше мы можем использовать блок Catch для конкретного типа ошибок, например при проблемах с доступом. Если в этом месте ничего не указывать — в этом блоке будут обрабатываться все варианты ошибок:
try {
'svchost111','svchost' | % {Get-Process -Name $PSItem -ErrorAction 'Stop'}
}
catch {
Write-Host "Какая-то неисправность" -ForegroundColor RED
}
Такой подход не рекомендуется использовать часто, так как вы можете пропустить что-то важное.
Мы можем вывести в блоке catch текст ошибки используя $PSItem.Exception:
try {
'svchost111','svchost' | % {Get-Process -Name $PSItem -ErrorAction 'Stop'}
}
catch {
Write-Host "Какая-то неисправность" -ForegroundColor RED
$PSItem.Exception
}
Переменная $PSItem хранит информацию о текущей ошибке, а глобальная переменная $Error будет хранит информацию обо всех ошибках. Так, например, я выведу одну и ту же информацию:
$Error[0].Exception
Создание отдельных исключений
Что бы обработать отдельную ошибку сначала нужно найти ее имя. Это имя можно увидеть при получении свойств и методов у значения переменной $Error:
$Error[0].Exception | Get-Member
Так же сработает и в блоке Catch с $PSItem:
Для вывода только имени можно использовать свойство FullName:
$Error[0].Exception.GetType().FullName
Далее, это имя, мы вставляем в блок Catch:
try {
'svchost111','svchost' | % {Get-Process -Name $PSItem -ErrorAction 'Stop'}
}
catch [Microsoft.PowerShell.Commands.ProcessCommandException]{
Write-Host "Произошла ошибка" -ForegroundColor RED
$PSItem.Exception
}
Так же, как и было описано выше мы можем усложнять эти блоки как угодно указывая множество исключений в одном catch.
Выброс своих исключений
Иногда нужно создать свои собственные исключения. Например мы можем запретить добавлять через какой-то скрипт названия содержащие маленькие буквы или сотрудников без указания возраста и т.д. Способов создать такие ошибки — два и они тоже делятся на критические и обычные.
Выброс с throw
Throw — выбрасывает ошибку, которая останавливает работу скрипта. Этот тип ошибок относится к критическим. Например мы можем указать только текст для дополнительной информации:
$name = 'AD.1'
if ($name -match '.'){
throw 'Запрещено использовать точки в названиях'
}
Если нужно, то мы можем использовать исключения, которые уже были созданы в Powershell:
$name = 'AD.1'
if ($name -like '*.*'){
throw [System.IO.FileNotFoundException]'Запрещено использовать точки в названиях'
}
Использование Write-Error
Команда Write-Error работает так же, как и ключ ErrorAction. Мы можем просто отобразить какую-то ошибку и продолжить выполнение скрипта:
$names = @('CL1', 'AD.1', 'CL3')
foreach ($name in $names){
if ($name -like '*.*'){
Write-Error -Message 'Обычная ошибка'
}
else{
$name
}
}
При необходимости мы можем использовать параметр ErrorAction. Значения этого параметра были описаны выше. Мы можем указать значение ‘Stop’, что полностью остановит выполнение скрипта:
$names = @('CL1', 'AD.1', 'CL3')
foreach ($name in $names){
if ($name -like '*.*'){
Write-Error -Message 'Обычная ошибка' -ErrorAction 'Stop'
}
else{
$name
}
}
Отличие команды Write-Error с ключом ErrorAction от обычных команд в том, что мы можем указывать исключения в параметре Exception:
Write-Error -Message 'Обычная ошибка' -ErrorAction 'Stop'
Write-Error -Message 'Исключение' -Exception [System.IO.FileNotFoundException] -ErrorAction 'Stop'
В Exception мы так же можем указывать сообщение. При этом оно будет отображаться в переменной $Error:
Write-Error -Exception [System.IO.FileNotFoundException]'Моё сообщение'
…
Теги:
#powershell
#ошибки
I want to have access to the same message that Powershell prints when you send an error record to the output stream
Example:
This is the exception message At
C:\Documents and
Settings\BillBillington\Desktop\psTest\exThrower.ps1:1
char:6
- throw <<<< (New-Object ArgumentException(«This is the
exception»));
- CategoryInfo : OperationStopped: (:) [],
ArgumentException- FullyQualifiedErrorId : This is the exception
I when a get the last ErrorRecord by doing $Error[0] I can’t seem to figure out how to get this information in a simple way
I found this ‘Resolve-Error’ function from the community extensions here which does roughly what I want but it prints a huge semi-formatted list of stuff I don’t need that I have to then strip
Is there way of accessing the message that Powershell uses or failing that a simpler way of getting hash of the values I care about so I can put them into a string in a format of my choosing?
Glorfindel
22k13 gold badges81 silver badges109 bronze badges
asked Aug 4, 2010 at 10:33
How about:
$x = ($error[0] | out-string)
Is that what you wanted?
answered Aug 4, 2010 at 12:47
tomasrtomasr
13.7k3 gold badges39 silver badges30 bronze badges
0
If you want a bit shorter message (more user friendly sometimes?) than @tomasr suggests this will do:
$error[0].ToString() + $error[0].InvocationInfo.PositionMessage
You will get something like:
Cannot find path 'C:\TEMP\_100804_135716\missing' because it does not exist.
At C:\TEMP\_100804_135716\test.ps1:5 char:15
+ Get-ChildItem <<<< missing
This technical info will be excluded:
+ CategoryInfo : ObjectNotFound: (C:\TEMP\_100804_135716\missing:String) [Get-ChildItem], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
answered Aug 4, 2010 at 13:13
Roman KuzminRoman Kuzmin
40.7k11 gold badges96 silver badges117 bronze badges
I took it a bit further because I didn’t like the multilines from $error[0].InvocationInfo.PositionMessage.
Function FriendlyErrorString ($thisError) {
[string] $Return = $thisError.Exception
$Return += "`r`n"
$Return += "At line:" + $thisError.InvocationInfo.ScriptLineNumber
$Return += " char:" + $thisError.InvocationInfo.OffsetInLine
$Return += " For: " + $thisError.InvocationInfo.Line
Return $Return
}
[string] $ErrorString = FriendlyErrorString $Error[0]
$ErrorString
You can look at what else is availible to construct your own String via:
$Error | Get-Member
$Error[0].InvocationInfo | Get-Member
answered Jun 18, 2013 at 16:58
KentKent
2813 silver badges3 bronze badges
Foreach ($Errors in $Error){
#Log Eintrag wird zusammengesetzt und in errorlog.txt geschrieben
"[$Date] $($Errors.CategoryInfo.Category) $($Errors.CategoryInfo.Activity) $($Errors.CategoryInfo.Reason) $($Errors.CategoryInfo.TargetName) $($Errors.CategoryInfo.TargetType) $($Errors.Exception.Message)" |Add-Content $Path\errorlog.txt -Encoding UTF8
}
You can also do this and you will get all Informations about the Error
answered Mar 17, 2017 at 13:17
Similar to @tomasr, but shorter:
$($error[0])
For all errors in a script:
$($error)
answered Dec 15, 2020 at 10:40
MinkusMinkus
3552 silver badges13 bronze badges
You have had it happen before. You run a PowerShell script, and suddenly the console is full of errors. Did you know you can handle these errors in a much better way? Enter PowerShell try catch blocks!
Using error handling with PowerShell try catch blocks allows for managing and responding to these terminating errors. In this post, you will be introduced to PowerShell try catch blocks and learn how to handle specific exception messages.
Understanding PowerShell Try Catch Syntax
The PowerShell try catch block syntax is straightforward. It is composed of two sections enclosed in curly brackets. The first identified section is the try
block, and the second section is the catch
block.
try { # Command(s) to try } catch { # What to do with terminating errors }
The try block can have as many statements in it as you want; however, keep the statements to as few as possible, probably just a single statement. The point of error handling is to work with one statement at a time and deal with anything that occurs from the error.
Here is an example of an error occurring in the PowerShell console. The command is creating a new file using the New-Item cmdlet and specifying a non-existent folder for Path.
If this command was in a script, the output wastes some screen space, and the problem may not be immediately visible. Using a PowerShell try catch block, you can manipulate the error output and make it more readable.
Here is the same New-Item command in a try catch block. Note that line 5 uses the -ErrorAction parameter with a value of Stop to the command. Not all errors are considered “terminating,” so sometimes you need to add this bit of code to terminate into the catch block properly.
try { New-Item -Path C:\doesnotexist ` -Name myfile.txt ` -ItemType File ` -ErrorAction Stop } catch { Write-Warning -Message "Oops, ran into an issue" }
Instead of a block of red angry-looking text, you have a simple warning message that it ran into an issue. The non-existent Path name along with forcing -ErrorAction Stop drops the logic into the catch block and displays the custom warning.
Adding the $Error Variable to Catch Output
While more readable, this is not very helpful. All you know is the command did not complete successfully, but you don’t know why. Instead of displaying my custom message, you can display the specific error message that occurred instead of the entire exception block.
When an error occurs in the try block, it is saved to the automatic variable named $Error. The $Error variable contains an array of recent errors, and you can reference the most recent error in the array at index 0.
try { New-Item -Path C:\doesnotexist ` -Name myfile.txt ` -ItemType File ` -ErrorAction Stop } catch { Write-Warning $Error[0] }
The warning output is now more descriptive showing that the command failed because it couldn’t find part of the path. This message was a part of our original error message but is now more concise.
Alternatively, you can save the incoming error to a variable using $_
. The dollar sign + underscore in PowerShell indicates the current item in the pipeline. In this case, the current item is the error message coming out of the try block.
Once you have saved this incoming message, you use it in a custom output message, like so:
try { New-Item -Path C:\doesnotexist ` -Name myfile.txt ` -ItemType File ` -ErrorAction Stop } catch { $message = $_ Write-Warning "Something happened! $message" }
Adding Exception Messages to Catch Blocks
You can also use multiple catch blocks to handle specific errors differently. This example displays two different custom messages:
- One for if the path does not exist
- One for if an illegal character is used in the name
Note that the following screenshot shows the script running twice with two different commands in the try block. Each command, catch block, and the orange and green arrows indicate the final output.
Looking at lines 14-16, there is a third catch block without an exception message. This is a “catch-all” block that will run if the error does not match any other exceptions. If you are running this script and seeing the message in the last catch block, you know the error is not related to an illegal character in the file name or part of the path not being valid.
Now how do you find the exception messages to use in the first two catch blocks? You find it by looking at the different information attached to the $Error
variable. After a failed command occurs, you can run $Error[0].Exception.GetType().FullName to view the exception message for the last error that occurred.
Going back to the PowerShell console, rerun the New-Item
command with a non-existent path, then run the $Error command to find the exception message.
The red text immediately following the failed command also contains the exception message but does not contain which module it is from. Looking at the $Error variable shows the full message to be used for a catch block.
Getting Exception Messages in PowerShell 7
PowerShell version 7 introduced the Get-Error
command. This command displays the most recent error from the current session. After encountering an error, run Get-Error to show the exception type to use in the catch
block.
Summary
Using PowerShell try catch blocks gives additional power to handle errors in a script and take different actions based on the error. The catch block can display more than just an error message. It can contain logic that will resolve the error and continue executing the rest of the script.
Do you have any tips on using try/catch blocks? Leave a comment below or find me on Twitter or LinkedIn for additional discussion.
Enjoyed this article? Check out more of my PowerShell articles here!
References:
Microsoft Docs: About Try Catch Finally
Microsoft Docs: About Automatic Variables ($Error)
Microsoft Blog: Understanding Non-Terminating Errors in PowerShell
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 use
powershell ... >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.)