Примечание:
Этот ответ о записи в stderr с точки зрения внешнего мира , когда оттуда вызывается сценарий PowerShell; хотя ответ написан с точки зрения оболочки Windows , cmd.exe
, он в равной степени относится к оболочкам Unix , таким как bash
, в сочетании с PowerShell Core.
Для сравнения: из в пределах Powershell , вы должны использовать Write-Error
, как объяснено в Ответ Кита Хилла .
К сожалению, существует нет унифицированный подход , который будет работать как от в PowerShell , так и извне - см. этот ответ моего для обсуждения.
Чтобы добавить к @ отличный ответ Криса Сира :
Хотя $host.ui.WriteErrorLine
должен работать на всех хостах, (по умолчанию) не записывает в stderr при вызове через cmd.exe
, например, из пакетного файла.
[Console]::Error.WriteLine
, напротив, всегда делает .
Итак, , если вы хотите написать сценарий PowerShell, который прекрасно воспроизводится с точки зрения потоков вывода при вызове из 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")
}
}
Дополнительная справочная информация:
Внутренне, PowerShell имеет больше , чем традиционные выходные потоки (stdout и stderr), и их количество со временем увеличивается (попробуйте Write-Warning "I'll go unheard." 3> $null
в качестве примера, и читайте больше в Get-Help about_Redirection
; обратите внимание, что связанная страница еще не отражает поток 6
для Write-Information
, представленный в PowerShell v5).
При взаимодействии с внешним миром PowerShell должен отображать нетрадиционные выходные потоки в 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
Команда выполняет запись во все выходные потоки PowerShell (при запуске в версии, предшествующей PowerShell-v5, вы увидите дополнительное сообщение об ошибке, относящееся к Write-Information
, которое было представлено в PowerShell v5), и имеет cmd.exe
перенаправить только стандартный вывод на NUL
(т. е. подавить вывод стандартный вывод; >NUL
).
Вы увидите вывод no , кроме 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
) или только с a stdout с перенаправлением (>...
или 1>...
) PowerShell отправляет все его выходные потоки в стандартный вывод .
При перенаправлении stderr (2>...
) PowerShell выборочно отправляет свой поток ошибок в stderr (независимо ото том, перенаправлен ли стандартный вывод или нет).
Как следствие, следующая общая идиома не работает должным образом:
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
Из этого следует, что PowerShell знает о перенаправлениях cmd.exe
и намеренно корректирует свое поведение .(Это также видно из того, что PowerShell создает цветной вывод в cmd.exe
консоли , одновременно обрабатывая цветовые коды, когда вывод перенаправляется в файл.)