Как написать стандартную ошибку в PowerShell?
У меня возникли проблемы с выяснением того, как и эхо в стандартный поток ошибок, и перенаправить поток ошибок исполняемого файла.
Я пришел из оболочки Bourne и оболочки Korn, из которых я бы использовал;
# Write to stderr
echo "Error Message!" >&2
# Redirect stderr to file
/do/error 2>/tmp/err.msg
Ответы
Ответ 1
Используйте Write-Error
для записи в stderr. Для перенаправления stderr на использование файла:
Write-Error "oops" 2> /temp/err.msg
или
exe_that_writes_to_stderr.exe bogus_arg 2> /temp/err.msg
Обратите внимание, что PowerShell записывает ошибки как записи ошибок. Если вы хотите избежать подробного вывода записей ошибок, вы можете сами записать информацию об ошибке:
PS> Write-Error "oops" -ev ev 2>$null
PS> $ev[0].exception
oops
-EV
является коротким (псевдоним) для -ErrorVariable
. Любые ошибки будут сохранены в переменной, названной аргументом этому параметру. PowerShell все равно сообщит об ошибке на консоль, если мы не перенаправим ошибку на $null
.
Ответ 2
Что вы, возможно, захотите, это следующее:
$host.ui.WriteErrorLine('I work in any PowerShell host')
Вы также можете увидеть следующее, но предполагается, что ваш хост PS является консолью
окно/устройство, поэтому я считаю это менее полезным:
[Console]::Error.WriteLine('I will not work in the PowerShell ISE GUI')
Ответ 3
Заметка:
-
Этот ответ касается написания stderr с точки зрения внешнего мира, когда оттуда вызывается скрипт PowerShell; в то время как ответ написан с точки зрения оболочки Windows cmd.exe
, он в равной степени относится к оболочкам Unix, таким как bash
сочетании с PowerShell Core.
-
Напротив, изнутри Powershell вы должны использовать Write-Error
, как объяснялось в ответе Кейта Хилла.
-
К сожалению, нет единого подхода, который будет работать как внутри PowerShell, так и со стороны - см. Этот мой ответ для обсуждения.
Чтобы добавить к @Chris Sear отличный ответ:
Хотя $host.ui.WriteErrorLine
должен работать на всех хостах, он не (по умолчанию) записывает stderr при вызове через cmd.exe
, например, из командного файла. [Console]::Error.WriteLine
, напротив, всегда делает это.
Поэтому, если вы хотите написать сценарий PS, который отлично воспроизводится с точки зрения выходных потоков при вызове из cmd.exe
, используйте следующую функцию Write-StdErr
, которая использует [Console]::Error.WriteLine
в обычном PS/cmd.exe
host (консольное окно) и $host.ui.WriteErrorLine
противном случае:
<#
.SYNOPSIS
Writes text to stderr when running in a regular console window,
to the host' 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")
}
}
Дополнительная справочная информация:
Внутренне, PS имеет больше, чем традиционные выходные потоки (stdout и stderr), и их число со временем увеличивается (попробуйте Write-Warning "I'll go unheard." 3> $null
в качестве примера и прочитайте больше в Get-Help about_Redirection
, обратите внимание, что связанная страница еще не отражает поток 6
для Write-Information
, представленный в PSv5).
При взаимодействии с внешним миром PS должен отображать нетрадиционные выходные потоки в stdout и stderr.
Странно, однако, PowerShell по умолчанию отправляет все свои потоки (включая вывод Write-Host
и $host.ui.WriteErrorLine()
) в stdout при вызове из cmd.exe
, даже если сопоставление потока ошибок PowerShell с stderr будет логичным выбором. Это поведение действует с (по крайней мере) v2 и по-прежнему применяется с v5.1 (и, вероятно, не изменится по соображениям обратной совместимости).
Вы можете проверить это с помощью следующей команды, если вы вызываете ее из 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
Команда записывает все выходные потоки PS (когда вы запускаете версию до версии PSv5, вы увидите сообщение об ошибке, относящееся к Write-Information
, которое было введено в PSv5) и имеет cmd.exe
перенаправление stdout только на NUL
( т.е. подавлять вывод stdout, >NUL
).
Вы не увидите выхода, кроме cerr
(из [Console]::Error.WriteLine()
, который напрямую записывается в stderr) - все потоки PowerShell были отправлены на stdout.
Возможно, даже более странно, можно захватить поток ошибок PowerShell, но только с перенаправлением:
Если вы измените >NUL
на 2>NUL
выше, это будет исключительно поток ошибок PowerShell и $host.ui.WriteErrorLine()
который будет подавлен; конечно, как и при любом перенаправлении, вы также можете отправить его в файл.
(Как указано, [Console]::Error.WriteLine()]
всегда выводит на stderr, будет ли последний перенаправлен или нет.)
Чтобы дать более целенаправленный пример (опять же, запустите cmd.exe
):
powershell -noprofile -command "'out'; Write-Error 'err'" 2>NUL
Выше выводит только out
- от Write-Error
вывода подавляется.
Подводя итог:
-
Без перенаправления (cmd.exe
) или только перенаправления stdout (>...
или 1>...
), PowerShell отправляет все свои выходные потоки в стандартный вывод.
-
С перенаправлением stderr (2>...
) PowerShell выборочно отправляет свой поток ошибок в stderr (независимо от того, перенаправляется ли stdout или нет).
-
Как следствие, следующая общая идиома не работает должным образом:
powershell... >data-output.txt
Это не будет, как можно было бы ожидать, отправлять только stdout для файла data-output.txt
при печати вывода stderr на терминал; вместо этого вам придется использовать
powershell... >data-output.txt 2>err-output.tmp; type err-output.tmp >&2; del err-output.tmp
Из этого следует, что PS осведомлен о перераспределении cmd.exe
и намеренно настраивает его поведение.
(Это также видно из PS, производящего цветной выход в консоли cmd.exe
то же время снимая цветовые коды, когда выход перенаправляется в файл.)
Если я что-то упустил и/или кто-то может дать обоснование дизайна для этого поведения, сообщите нам об этом.