如何在没有附带堆栈跟踪的情况下在 PowerShell 中显示 'naked' 错误消息?

How can I display a 'naked' error message in PowerShell without an accompanying stacktrace?

如何从 PowerShell 写入标准错误,或捕获以下错误:

这些年来,我一直在 throw 错误或通过 Write-Error 编写,但我又累又老,在我的脚本中我只想看到一条简明的错误消息.我一直在尝试 trapthrowWrite-Error-ErrorAction 的所有组合,但都无济于事:

try {
  throw "error" # Sample code for a stack overflow. In the theater
  # of your mind, imagine there is code here that does something real and useful
} catch {
  Write-Error "An error occurred attempting to 'do something.' Have you tried rebooting?"
}

这是我想看到的用户体验:

C:\> & .\Do-Something.ps1
An error occurred attempting to 'do something.' Have you tried rebooting?

C:\> ▏

相反,我得到:

C:\> & .\Do-Something.ps1
An error occurred attempting to 'do something.' Have you tried rebooting?
At line:1 char:1
+ Do-RealWork
+ ~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Do-RealWork

C:\> ▏

将自动 $ErrorView 变量设置为 'CategoryView' 会导致 PowerShell 输出简洁的 单行 错误表示,但这表示可能并不总是包含 足够 信息,因为错误 message 通常 not 包含在内;从好的方面来说,传递给 Throw "..." 的文本是 反映出来的,但是相比之下,Write-Error 输出包含 no 'CategoryView' 期间的具体信息。
向单行但始终包含所有关键信息的 PowerShell 添加新的错误视图是 being discussed for v6.

假设您的 PowerShell 代码是 运行 来自 console(使用控制台主机),使用[Console]::Error.WriteLine()无条件写入外部世界的 stderr(标准错误流):

[Console]::Error.WriteLine("An error occurred ... Have you tried rebooting?")

注:

  • 这不适用于非控制台主机,例如 PowerShell ISE。

  • [Console]::Error.WriteLine() 输出未在控制台 red 中打印 [1].


可悲的是,没有一种解决方案既适用于 PowerShell(跨主机)来自之外:

  • [Console]::Error.WriteLine(),在为外部世界正确写入 stderr 时,无法捕获或抑制其输出内部 PowerShell,并且仅适用于 PowerShell console 主机。

  • 类似地,$host.ui.WriteErrorLine(),即使适用于 所有 主机,它也是 UI 方法也在 PowerShell 的流系统之外工作,因此其输出也无法在 PowerShell 中捕获或抑制。
    更重要的是,它不会写入外部世界的 stderr(在这方面它的行为类似于 Write-Error,见下文)。

  • PowerShell中,只有Write-Error写入PowerShell的错误流,所以它的输出可以是捕获/压制。
    然而,不幸的是,Write-Error(除了吵之外)确实写入外界的stderr,unless,奇怪的是,stderr是明确被重定向 - 详情请参阅我的 this answer


[1] Peter(OP 本人)为此提供了一个解决方法:

[Console]::ForegroundColor = 'red'
[Console]::Error.WriteLine("An error occurred ... Have you tried rebooting?")
[Console]::ResetColor()

为它提供了一个函数包装器。

幸运的是,PowerShell 在检测到输出被重定向(到文件)时会自动忽略颜色代码。

我认为在 PowerShell 中捕获错误的最佳方法是使用以下方法:

$Error[0].Exception.GetType().FullName

这里有一个如何正确使用它的例子。基本上测试您在 PowerShell 中尝试在脚本失败的不同场景中执行的操作。

这是一条典型的 PowerShell 错误消息:

PS C:\> Stop-Process -Name 'FakeProcess'
Stop-Process : Cannot find a process with the name "FakeProcess". Verify the process name and call the cmdlet again.
At line:1 char:1
+ Stop-Process -Name 'FakeProcess'
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (FakeProcess:String) [Stop-Process], ProcessCommandException
    + FullyQualifiedErrorId : NoProcessFoundForGivenName,Microsoft.PowerShell.Commands.StopProcessCommand

接下来你会得到异常的错误信息:

PS C:\> $Error[0].Exception.GetType().FullName
Microsoft.PowerShell.Commands.ProcessCommandException

您可以按如下方式设置代码以捕获错误消息:

Try
{
    #-ErrorAction Stop is needed to go to catch statement on error
    Get-Process -Name 'FakeProcess' -ErrorAction Stop
}
Catch [Microsoft.PowerShell.Commands.ProcessCommandException]
{
    Write-Host "ERROR: Process Does Not Exist. Please Check Process Name"
}

输出将如下所示,而不是上面示例中的 Powershell 标准错误:

ERROR: Process Does Not Exist. Please Check Process Name

最后,您还可以使用多个 catch 块来处理代码中的多个错误。您还可以包含一个 "blanket" catch 块来捕获您尚未处理的所有错误。示例:

Try
{
    Get-Process -Name 'FakeProcess' -ErrorAction Stop
}

Catch [Microsoft.PowerShell.Commands.ProcessCommandException]
{
    Write-Host "ERROR: Process Does Not Exist. Please Check Process Name"
}

Catch [System.Exception]
{
    Write-Host "ERROR: Some Error Message Here!"
}

Catch
{
    Write-Host "ERROR: I am a blanket catch to handle all unspecified errors you aren't handling yet!"
}

基于 ,您可以使用自定义函数临时覆盖内置的 Write-Error cmdlet。

# Override the built-in cmdlet with a custom version
function Write-Error($message) {
    [Console]::ForegroundColor = 'red'
    [Console]::Error.WriteLine($message)
    [Console]::ResetColor()
}

# Pretty-print "Something is wrong" on stderr (in red).
Write-Error "Something is wrong"

# Setting things back to normal 
Remove-Item function:Write-Error

# Print the standard bloated Powershell errors
Write-Error "Back to normal errors"

这样您就可以利用 Powershell 函数优先于 cmdlet 的事实。

https://technet.microsoft.com/en-us/library/hh848304.aspx

这是我能够想到的最优雅的方法,既可以显示美观简洁的错误消息,又可以让 TeamCity 轻松检测问题。

我最近需要自己解决这个问题,所以我整理了一个 Write-ErrorMessage 函数,详情如下:https://intellitect.com/powershell-write-error-without-writing-stack-trace/

具体来说,我利用了组合

Write-Error -Message $err -ErrorAction SilentlyContinue
$Host.UI.WriteErrorLine($errorMessage)

基于 我编写了以下函数,让您可以毫不费力地将 Write-Error 与自定义函数交换并返回。我还添加了检查用户是否从 PowerShell ISE

调用写入错误
# Override the built-in cmdlet with a custom version
function New-ErrorFunc {
        function Dyn($message){
            param($message,$ErrorAction)
            if($psISE){
                $Host.UI.WriteErrorLine($message)
            }
            else{
            [Console]::ForegroundColor = 'red'
            [Console]::Error.WriteLine($message)
            [Console]::ResetColor()
            }
           if($ErrorAction -eq 'Stop'){
           Break
           }
        }

    return ${function:Dyn}
}
function Set-ErrorFunc(){
    param([bool]$custom=$true)
    if($custom){
    $dynfex= New-ErrorFunc
    Invoke-Expression -Command "function script:Write-Error{ $dynfex }"
    }
    else {
        $custom= Get-Command Write-Error | Where-Object {$_.CommandType -eq 'Function'} 
        if($custom){ Remove-Item function:Write-Error }
   }
}

#User our Custom Error Function
Set-ErrorFunc 
# Pretty-print "Something is wrong" on stderr (in red).
Write-Error "Something is wrong"

# Setting things back to normal 
Set-ErrorFunc -custom $false
# Print the standard bloated Powershell errors
Write-Error "Back to normal errors"