【问题标题】:How can I display a 'naked' error message in PowerShell without an accompanying stacktrace?如何在没有附带堆栈跟踪的情况下在 PowerShell 中显示“裸”错误消息?
【发布时间】:2016-10-30 02:26:38
【问题描述】:

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

  • 错误消息显示为错误(真正写入标准错误以便TeamCity 和 Octopus 将其视为错误)
  • 没有堆栈跟踪垃圾会混淆我漂亮、简洁的错误消息

这些年来,我在throwing 错误或通过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:\> ▏

【问题讨论】:

  • FWIW $error[0].Exception.Message 包含错误消息,但我不确定这对您是否有用。

标签: powershell error-handling stderr exit-code


【解决方案1】:

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

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

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

注意:

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

  • [Console]::Error.WriteLine() 输出不会在控制台[1]中以红色打印。


遗憾的是,没有一种单一的解决方案可以同时在内部 PowerShell(跨主机)外部工作:

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

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

  • 在 PowerShell 内部,只有 Write-Error 写入 PowerShell 的错误流,因此可以捕获/抑制其输出
    然而,不幸的是,Write-Error(除了嘈杂之外)确实写入外部世界的 stderr,除非,奇怪的是,stderr 被显式重定向 em> - 详情请参阅我的this answer


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

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

suneg's helpful answer 为其提供了一个函数包装器。

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

【讨论】:

  • 很好的答案。在不添加新答案的情况下,我想推荐另一种方式,而不是使用[Console]::Error.WriteLine。除了它在 ISE 中不起作用之外,它不会添加到 $Error 变量中。如果我曾经使用 Write-Error,那是因为我想在以后使用 $Error 变量处理错误,然后我会完全隐藏输出:Write-Error "hmm" -ErrorAction SilentlyContinue,并且还可以选择在该点以红色显示错误文本,我将使用Write-Host "hmm" -ForegroundColor Red,但请注意,Write-Host 文本不会通过管道传输,也无法重定向到文件
  • 谢谢,@papo。在 PSv5+ 中,您可以使用 6> 重定向 Write-Host 输出
【解决方案2】:

idea in a previous answer 的基础上,您可以使用自定义函数临时覆盖内置的 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 轻松检测问题。

【讨论】:

    【解决方案3】:

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

    具体来说,我利用了这种组合

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

    【讨论】:

    • 欢迎提供解决方案链接,但请确保您的答案在没有它的情况下有用:add context around the link 这样您的其他用户就会知道它是什么以及为什么会出现,然后引用最相关的您链接到的页面的一部分,以防目标页面不可用。 Answers that are little more than a link may be deleted.
    • 只是 $Host.UI.WriteErrorLine(),而不是 Write-Error 在 Sitecore PSE 中为我解决了问题
    【解决方案4】:

    在我看来,在 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 块来处理代码中的多个错误。您还可以包含一个“毯子”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-Output 写入错误消息是不明智的,因为它会写入 PowerShell 的 success(输出)流。除此之外,您的帖子更侧重于异常处理本身,这不是 OP 的问题所在(他清楚地知道如何捕获异常,如他的问题所示)。
    • Write-Host 会是更好的选择吗?
    • 老实说,我希望有人过来告诉我,我可以在 $host 上设置一个可以抑制堆栈跟踪的设置。我真的需要真正的错误(写入 stderr),以便将错误冒泡到寻找错误输出的地方(如 TeamCity 构建)。在 TeamCity 的 20000 行构建日志中查找错误消息很难;这就是为什么我们需要将它写入标准错误
    • @TylerHelder: Write-Host 稍微好一点,因为它不会污染输出流,但最终你想要写入专用 error 流的东西——两者都来自PowerShell 自己的观点和外界的观点。可悲的是,即使是 PowerShell 自己的(罗嗦的)Write-Error 也不满足这些标准。
    • @PeterSeale:将$ErrorView 首选项变量设置为'CategoryView' 会输出更简洁的错误表示,但是,唉,这通常是不够的,因为实际的错误 message 不见了。
    【解决方案5】:

    基于suneg's answer,我编写了以下函数,让您可以轻松地将 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"
    

    【讨论】:

      猜你喜欢
      • 2013-12-31
      • 2019-09-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-12
      • 2015-08-04
      • 2018-08-20
      • 2016-12-02
      相关资源
      最近更新 更多