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

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

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

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

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

3 ответа

Использование 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,

Замечания:

  • Этот ответ о записи в 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.ui.WriteErrorLine иначе:

<#
.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")
  }
}

Дополнительная справочная информация:

Внутренне 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() вывод) в стандартный вывод при вызове из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 перенаправить стандартный вывод только на NUL (то есть подавить вывод stdout; >NUL).

Вы не увидите ничего, кроме cerr (от [Console]::Error.WriteLine(), который пишет напрямую в stderr) - все потоки PowerShell были отправлены на стандартный вывод.

Возможно, еще более странно, что можно перехватить поток ошибок 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
    Это не будет, как можно было ожидать, отправлять только стандартныйвывод в файл 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 консоль при удалении цветовых кодов, когда вывод перенаправляется в файл.)

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

Что вы, вероятно, хотите, это:

$host.ui.WriteErrorLine('I work in any PowerShell host')

Вы также можете увидеть следующее, но предполагается, что ваш хост PS является консольным окном / устройством, поэтому я считаю его менее полезным:

[Console]::Error.WriteLine('I will not work in the PowerShell ISE GUI')
Другие вопросы по тегам