如何在PowerShell中写入标准错误?

Bre*_*yan 41 powershell stderr powershell-2.0

我无法弄清楚如何回显标准错误流并重定向可执行文件的错误流.

我来自Bourne shellKorn shell背景,我将使用它;

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

# Redirect stderr to file
/do/error 2>/tmp/err.msg
Run Code Online (Sandbox Code Playgroud)

Kei*_*ill 42

使用Write-Error写入标准错误.要将stderr重定向到文件使用:

 Write-Error "oops" 2> /temp/err.msg
Run Code Online (Sandbox Code Playgroud)

要么

 exe_that_writes_to_stderr.exe bogus_arg 2> /temp/err.msg
Run Code Online (Sandbox Code Playgroud)

请注意,PowerShell将错误写为错误记录.如果您想避免错误记录的详细输出,您可以自己写出错误信息,如下所示:

PS> Write-Error "oops" -ev ev 2>$null
PS> $ev[0].exception
oops
Run Code Online (Sandbox Code Playgroud)

-EV是短(别名)-ErrorVariable.任何错误都将存储在此参数的参数所指定的变量中.除非我们将错误重定向到,否则PowerShell仍会将错误报告给控制台$null.

  • 不幸的是,这不是我想要的,我不希望出现堆栈跟踪,我只想用一条错误消息写入错误流。 (2认同)
  • 看来 `Write-Error "oops" -ev ev 2>&1 > $null` 不会将任何内容重定向到 `stderr`。`Write-Error "oops"` 效果很好,但你却被冗长的输出所困扰。 (2认同)

Chr*_*ars 33

你可能想要的是这个:

$host.ui.WriteErrorLine('I work in any PowerShell host')
Run Code Online (Sandbox Code Playgroud)

您可能还会看到以下内容,但它假设您的PS主机是控制台窗口/设备,因此我认为它不太有用:

[Console]::Error.WriteLine('I will not work in the PowerShell ISE GUI')
Run Code Online (Sandbox Code Playgroud)

  • `WriteErrorLine`是个坏主意.查看文档(https://msdn.microsoft.com/en-us/library/system.management.automation.host.pshostuserinterface.writeerrorline%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396) :*此方法将一行写入主机的错误显示.*换句话说,这是一个**UI**方法,有时按预期工作并重定向到stderr,有时(大多数情况下?)没有' t(参见mklement0的回答:http://stackoverflow.com/a/15669365/67824).正如Keith Hill所说,"Write-Error"是最好的选择. (2认同)

mkl*_*nt0 30

注意:

  • 这个答案是关于从那里调用PowerShell脚本时外部世界的角度写入stderr ; 虽然答案是从Windows shell 的角度编写的cmd.exe,但它同样适用于Unix shell,例如bash与PowerShell Core结合使用时.

  • 相比之下, Powershell的,你应该使用Write-Error,如在解释基思·希尔的回答.

  • 遗憾的是,没有统一的方法可以 PowerShell 外部工作 - 请参阅我的这个答案进行讨论.


要添加@Chris Sear的好答案:

虽然$host.ui.WriteErrorLine应该在所有主机中工作,但它(默认情况下)在调用时不会写入stderrcmd.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")
  }
}
Run Code Online (Sandbox Code Playgroud)

可选背景信息:

在内部,PS具有比传统的输出流(stdout和stderr),其数量已增加了时间(尝试Write-Warning "I'll go unheard." 3> $null作为一个例子,而更多的Get-Help about_Redirection,请注意,链接的网页还没有反映流6Write-Information,在PSv5介绍).

当与外界连接时,PS必须将非传统输出流映射到stdout和stderr.

然而,奇怪的是,PowerShell默认情况下会在调用时将其所有流(包括Write-Host$host.ui.WriteErrorLine()输出)发送到stdoutcmd.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
Run Code Online (Sandbox Code Playgroud)

该命令写入所有PS输出流(当您在PSv5之前的版本上运行时,您将看到与Write-InformationPSv5中引入的其他错误消息相关,并且cmd.exestdout重定向到NUL(即,抑制 stdout输出; >NUL) .

除了(from ,直接写入stderr)之外,您将看不到任何输出- 所有PowerShell的流都被发送到stdout.cerr[Console]::Error.WriteLine()

也许更奇怪的是,可能捕获PowerShell的错误流,但只能使用重定向:

如果更改>NUL2>NUL上面的内容,那么PowerShell的错误流$host.ui.WriteErrorLine()输出将被禁止; 当然,与任何重定向一样,您也可以将其发送到文件.
(如上所述,[Console]::Error.WriteLine()] 始终输出到stderr,无论后者是否被重定向.)

给出一个更有针对性的例子(再次,运行cmd.exe):

powershell -noprofile -command "'out'; Write-Error 'err'" 2>NUL
Run Code Online (Sandbox Code Playgroud)

以上只输出out- Write-Error的输出被抑制.

总结一下:

  • 没有任何的(cmd.exe)重定向或只有一个标准输出重定向(>...1>...),PowerShell的将所有的输出流stdout中.

  • 使用stderr重定向(2>...),PowerShell 有选择地将其错误流发送到stderr(无论stdout是否也被重定向).

  • 作为推论,下面的常用习惯用法不能按预期工作:
    powershell ... >data-output.txt
    正如人们所预料的那样,这不会在将stderr输出打印到终端时仅向文件发送stdout ; 相反,你必须使用data-output.txt
    powershell ... >data-output.txt 2>err-output.tmp; type err-output.tmp >&2; del err-output.tmp

因此,PS意识到了cmd.exe重定向并故意调整其行为.
(这也可以从PS 在控制台中生成彩色输出,同时在输出重定向到文件时剥离颜色代码.)cmd.exe

如果我遗漏了某些内容和/或某人可以提供此行为的设计理由,请告诉我们.