【问题标题】:Catching FULL exception message捕获完整的异常消息
【发布时间】:2016-07-17 08:31:26
【问题描述】:

考虑:

Invoke-WebRequest $sumoApiURL -Headers @{"Content-Type"= "application/json"} -Credential $cred -WebSession $webRequestSession -Method post -Body $sumojson -ErrorAction Stop

这会引发以下异常:

如何才能完全捕捉到它或至少过滤掉“已存在同名资源。”?

使用$_.Exception.GetType().FullName 产生

System.Net.WebException

$_.Exception.Message 给了

远程服务器返回错误:(400) Bad Request。

【问题讨论】:

    标签: powershell exception exception-handling


    【解决方案1】:

    PowerShell 中的错误和异常是结构化对象。您在控制台上看到的错误消息实际上是一条格式化消息,其中包含来自错误/异常对象的多个元素的信息。您可以像这样自己(重新)构建它:

    $formatstring = "{0} : {1}`n{2}`n" +
                    "    + CategoryInfo          : {3}`n" +
                    "    + FullyQualifiedErrorId : {4}`n"
    $fields = $_.InvocationInfo.MyCommand.Name,
              $_.ErrorDetails.Message,
              $_.InvocationInfo.PositionMessage,
              $_.CategoryInfo.ToString(),
              $_.FullyQualifiedErrorId
    
    $formatstring -f $fields
    

    如果您只想在 catch 块中显示错误消息,您可以简单地回显当前对象变量(此时包含错误):

    try {
      ...
    } catch {
      $_
    }
    

    如果您需要彩色输出,请使用带有上述格式化字符串的Write-Host

    try {
      ...
    } catch {
      ...
      Write-Host -Foreground Red -Background Black ($formatstring -f $fields)
    }
    

    话虽如此,通常您不想在异常处理程序中按原样显示错误消息(否则-ErrorAction Stop 将毫无意义)。结构化错误/异常对象为您提供可用于更好地控制错误的附加信息。例如,您有 $_.Exception.HResult 与实际错误号。 $_.ScriptStackTrace$_.Exception.StackTrace,所以你可以在调试时显示堆栈跟踪。 $_.Exception.InnerException 使您可以访问嵌套异常,这些异常通常包含有关错误的附加信息(顶级 PowerShell 错误可能有些通用)。您可以通过以下方式展开这些嵌套异常:

    $e = $_.Exception
    $msg = $e.Message
    while ($e.InnerException) {
      $e = $e.InnerException
      $msg += "`n" + $e.Message
    }
    $msg
    

    在您的情况下,您要提取的信息似乎在 $_.ErrorDetails.Message 中。我不太清楚那里是否有对象或 JSON 字符串,但您应该能够通过运行获取有关 $_.ErrorDetails 成员的类型和值的信息

    $_.ErrorDetails | Get-Member
    $_.ErrorDetails | Format-List *
    

    如果$_.ErrorDetails.Message 是一个对象,您应该能够像这样获得消息字符串:

    $_.ErrorDetails.Message.message
    

    否则需要先将JSON字符串转为对象:

    $_.ErrorDetails.Message | ConvertFrom-Json | Select-Object -Expand message
    

    根据您要处理的错误类型,特定类型的异常还可能包含有关当前问题的更具体信息。例如,在您的情况下,您有一个 WebException,除了错误消息 ($_.Exception.Message) 之外,它还包含来自服务器的实际响应:

    PS C:\> $e.Exception |获取会员
    
       类型名称:System.Net.WebException
    
    名称 MemberType 定义
    ---- ---------- ----------
    Equals 方法 bool Equals(System.Object obj), bool _Exception.E...
    GetBaseException 方法 System.Exception GetBaseException(), System.Excep...
    GetHashCode 方法 int GetHashCode(), int _Exception.GetHashCode()
    GetObjectData 方法 void GetObjectData(System.Runtime.Serialization.S...
    GetType 方法类型 GetType(),类型 _Exception.GetType()
    ToString 方法 string ToString(), string _Exception.ToString()
    数据属性 System.Collections.IDictionary 数据 {get;}
    HelpLink 属性字符串 HelpLink {get;set;}
    HResult 属性 int HResult {get;}
    InnerException 属性 System.Exception InnerException {get;}
    消息属性字符串消息 {get;}
    响应属性 System.Net.WebResponse 响应 {get;}
    Source 属性字符串 Source {get;set;}
    StackTrace 属性字符串 StackTrace {get;}
    状态属性 System.Net.WebExceptionStatus 状态 {get;}
    TargetSite 属性 System.Reflection.MethodBase TargetSite {get;}

    它为您提供如下信息:

    PS C:\> $e.Exception.Response
    
    IsMutuallyAuthenticated : 假
    饼干                 : {}
    标头:{Keep-Alive、Connection、Content-Length、Content-T...}
    SupportsHeaders:真
    内容长度:198
    内容编码:
    内容类型:文本/html;字符集=iso-8859-1
    字符集:iso-8859-1
    服务器:Apache/2.4.10
    最后修改时间:17.07.2016 14:39:29
    状态码:未找到
    状态描述:未找到
    协议版本:1.1
    ResponseUri:http://www.example.com/
    方法:POST
    IsFromCache : 假

    由于并非所有异常都具有完全相同的一组属性,因此您可能希望对特定异常使用特定的处理程序:

    try {
      ...
    } catch [System.ArgumentException] {
      # handle argument exceptions
    } catch [System.Net.WebException] {
      # handle web exceptions
    } catch {
      # handle all other exceptions
    }
    

    如果您有需要完成的操作,无论是否发生错误(清理任务,如关闭套接字或数据库连接),您可以在异常处理后将它们放在finally 块中:

    try {
      ...
    } catch {
      ...
    } finally {
      # cleanup operations go here
    }
    

    【讨论】:

    • 我已经找到了答案,但这要详细得多。干杯。
    • 如果您认为 PowerShell 的默认错误格式旨在激怒 C# 工程师并鼓励他们将 PowerShell 扔进黑洞,请投票。
    • @BrainSlugs83 $ErrorActionPreference = 'Stop'; try {Write-Error 'foo'} catch {$_.GetType().FullName}你说的是?
    • 这很奇怪。它的行为仍然与throw 不同。 -- throw 与 try/catch 一起工作,即使 $ErrorActionPreferenceContinue,而这似乎只有在设置为 Stop 时才有效? -- 我没有意识到non-terminating 在你之前的声明中很重要。 -- 谢谢你给我看这个,我今天学到了一些新东西。 :-)
    • @BrainSlugs83 RelatedAlso related.
    【解决方案2】:

    我找到了!

    只需打印出$Error[0] 即可获得最后一条错误消息。

    【讨论】:

      【解决方案3】:

      以下对我来说效果很好

      try {
          asdf
      } catch {
          $string_err = $_ | Out-String
      }
      
      write-host $string_err
      

      这样的结果是下面的字符串而不是 ErrorRecord 对象

      asdf : The term 'asdf' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
      At C:\Users\TASaif\Desktop\tmp\catch_exceptions.ps1:2 char:5
      +     asdf
      +     ~~~~
          + CategoryInfo          : ObjectNotFound: (asdf:String) [], CommandNotFoundException
          + FullyQualifiedErrorId : CommandNotFoundException
      

      【讨论】:

      • 这是最好的方法 - 它同时捕获“throw ”和调用。
      • 完美,它提供了我需要的所有信息,尤其是错误的行号。
      • 太棒了...比什么都容易:)
      【解决方案4】:

      我不断回到这些问题上,试图找出我感兴趣的数据到底埋藏在真正的单体 ErrorRecord 结构中的什么位置。几乎所有答案都给出了关于如何提取某些数据位的零碎说明。

      但我发现使用ConvertTo-Json 转储整个对象非常有帮助,这样我就可以在可理解的布局中直观地看到所有内容。

          try {
              Invoke-WebRequest...
          }
          catch {
              Write-Host ($_ | ConvertTo-Json)
          }
      

      使用ConvertTo-Json-Depth 参数来扩展更深的值,但在超过默认深度2 时要格外小心:P

      https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/convertto-json

      【讨论】:

      • ConvertTo-Json 让我失望了,The type 'System.Collections.ListDictionaryInternal' is not supported for serialization or deserialization of a dictionary. Keys must be strings.;我改用Out-String 得到了我需要的东西。
      • 您可能需要使用 -Depth 标志来限制 JSON 的深度。我有 JSON 解析的例子,在 Catch 语句中使用 GB 的 RAM 转换 ErrorRecord 对象......
      【解决方案5】:

      您可以添加:

      -ErrorVariable errvar
      

      然后查看$errvar

      【讨论】:

        【解决方案6】:

        选项 1:简单但有效,足以满足大多数目的

        try {1/0} catch { $_ | Format-List * -Force | Out-String }

        结果:

        PSMessageDetails      :
        Exception             : System.Management.Automation.RuntimeException: Attempted to divide by zero. ---> System.DivideByZeroException: Attempted to divide by zero.
                                   --- End of inner exception stack trace ---
                                   at System.Management.Automation.ExceptionHandlingOps.CheckActionPreference(FunctionContext funcContext, Exception exception)
                                   at System.Management.Automation.Interpreter.ActionCallInstruction`2.Run(InterpretedFrame frame)
                                   at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
                                   at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
        TargetObject          :
        CategoryInfo          : NotSpecified: (:) [], RuntimeException
        FullyQualifiedErrorId : RuntimeException
        ErrorDetails          :
        InvocationInfo        : System.Management.Automation.InvocationInfo
        ScriptStackTrace      : at <ScriptBlock>, <No file>: line 1
        PipelineIterationInfo : {}
        
        

        选项 2:同时打印调用信息

        try {1/0} catch { $_ | Format-List * -Force | Out-String ; $_.InvocationInfo | Format-List * -Force | Out-String }

        结果:

        PSMessageDetails      :
        Exception             : System.Management.Automation.RuntimeException: Attempted to divide by zero. ---> System.DivideByZeroException: Attempted to divide by zero.
                                   --- End of inner exception stack trace ---
                                   at System.Management.Automation.ExceptionHandlingOps.CheckActionPreference(FunctionContext funcContext, Exception exception)
                                   at System.Management.Automation.Interpreter.ActionCallInstruction`2.Run(InterpretedFrame frame)
                                   at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
                                   at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
        TargetObject          :
        CategoryInfo          : NotSpecified: (:) [], RuntimeException
        FullyQualifiedErrorId : RuntimeException
        ErrorDetails          :
        InvocationInfo        : System.Management.Automation.InvocationInfo
        ScriptStackTrace      : at <ScriptBlock>, <No file>: line 1
        PipelineIterationInfo : {}
        
        
        
        
        
        MyCommand             :
        BoundParameters       : {}
        UnboundArguments      : {}
        ScriptLineNumber      : 1
        OffsetInLine          : 6
        HistoryId             : -1
        ScriptName            :
        Line                  : try {1/0} catch { $_ | Format-List * -Force | Out-String ; $_.InvocationInfo | Format-List * -Force | Out-String }
        PositionMessage       : At line:1 char:6
                                + try {1/0} catch { $_ | Format-List * -Force | Out-String ; $_.Invocat ...
                                +      ~~~
        PSScriptRoot          :
        PSCommandPath         :
        InvocationName        :
        PipelineLength        : 0
        PipelinePosition      : 0
        ExpectingInput        : False
        CommandOrigin         : Internal
        DisplayScriptPosition :
        

        选项 3:以上都加上所有内部异常

        try {1/0} catch { $Exception = $_; $Exception | Format-List * -Force | Out-String ; $Exception.InvocationInfo | Format-List * -Force | Out-String ; for ($i = 0; $Exception; $i++, ($Exception = $Exception.InnerException)) { Write-Host ("$i" * 80) ; $Exception | Format-List * -Force | Out-String } }

        PSMessageDetails      :
        Exception             : System.Management.Automation.RuntimeException: Attempted to divide by zero. ---> System.DivideByZeroException: Attempted to divide by zero.
                                   --- End of inner exception stack trace ---
                                   at System.Management.Automation.ExceptionHandlingOps.CheckActionPreference(FunctionContext funcContext, Exception exception)
                                   at System.Management.Automation.Interpreter.ActionCallInstruction`2.Run(InterpretedFrame frame)
                                   at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
                                   at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
        TargetObject          :
        CategoryInfo          : NotSpecified: (:) [], RuntimeException
        FullyQualifiedErrorId : RuntimeException
        ErrorDetails          :
        InvocationInfo        : System.Management.Automation.InvocationInfo
        ScriptStackTrace      : at <ScriptBlock>, <No file>: line 1
        PipelineIterationInfo : {}
        
        
        
        
        
        MyCommand             :
        BoundParameters       : {}
        UnboundArguments      : {}
        ScriptLineNumber      : 1
        OffsetInLine          : 6
        HistoryId             : -1
        ScriptName            :
        Line                  : try {1/0} catch { $Exception = $_; $Exception | Format-List * -Force | Out-String ; $Exception.InvocationInfo | Format-List * -Force | Out-String ; for ($i = 0; $Exception;
                                $i++, ($Exception = $Exception.InnerException)) { Write-Host ("$i" * 80) ; $Exception | Format-List * -Force | Out-String  }  }
        PositionMessage       : At line:1 char:6
                                + try {1/0} catch { $Exception = $_; $Exception | Format-List * -Force  ...
                                +      ~~~
        PSScriptRoot          :
        PSCommandPath         :
        InvocationName        :
        PipelineLength        : 0
        PipelinePosition      : 0
        ExpectingInput        : False
        CommandOrigin         : Internal
        DisplayScriptPosition :
        
        
        
        
        00000000000000000000000000000000000000000000000000000000000000000000000000000000
        
        
        PSMessageDetails      :
        Exception             : System.Management.Automation.RuntimeException: Attempted to divide by zero. ---> System.DivideByZeroException: Attempted to divide by zero.
                                   --- End of inner exception stack trace ---
                                   at System.Management.Automation.ExceptionHandlingOps.CheckActionPreference(FunctionContext funcContext, Exception exception)
                                   at System.Management.Automation.Interpreter.ActionCallInstruction`2.Run(InterpretedFrame frame)
                                   at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
                                   at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
        TargetObject          :
        CategoryInfo          : NotSpecified: (:) [], RuntimeException
        FullyQualifiedErrorId : RuntimeException
        ErrorDetails          :
        InvocationInfo        : System.Management.Automation.InvocationInfo
        ScriptStackTrace      : at <ScriptBlock>, <No file>: line 1
        PipelineIterationInfo : {}
        

        【讨论】:

          猜你喜欢
          • 2011-06-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-11-01
          • 2013-02-01
          • 1970-01-01
          • 1970-01-01
          • 2014-08-08
          相关资源
          最近更新 更多