Подтвердить что ты не робот

Как написать стандартную ошибку в PowerShell?

У меня возникли проблемы с выяснением того, как и эхо в стандартный поток ошибок, и перенаправить поток ошибок исполняемого файла.

Я пришел из оболочки Bourne и оболочки Korn, из которых я бы использовал;

# Write to stderr
echo "Error Message!" >&2

# Redirect stderr to file
/do/error 2>/tmp/err.msg
4b9b3361

Ответ 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 то же время снимая цветовые коды, когда выход перенаправляется в файл.)

Если я что-то упустил и/или кто-то может дать обоснование дизайна для этого поведения, сообщите нам об этом.