我可以在PowerShell中获得详细的exception堆栈跟踪吗?

运行这样的脚本:

1: function foo() 2: { 3: bar 4: } 5: 6: function bar() 7: { 8: throw "test" 9: } 10: 11: foo 

我懂了

 test At C:\test.ps1:8 char:10 

我可以得到一个详细的堆栈跟踪吗?

 At bar() in C:\test.ps1:8 At foo() in C:\test.ps1:3 At C:\test.ps1:11 

有自动variables$StackTrace但它似乎是一个更具体的内部PS细节比实际上关心你的脚本,所以这不会有太大的帮助。

还有Get-PSCallStack但不幸的是,只要你遇到exception,就会消失。 但是,您可以在每次投掷脚本之前放置Get-PSCallStack 。 这样你在遇到exception之前立即得到一个堆栈跟踪。

我想通过使用Powershell的debugging和跟踪function可以编写这样的function,但是我怀疑这很容易。

PowerShell Team博客上有一个名为Resolve-Error的函数 ,它将为您提供各种细节

请注意,$ error是您在PSSession中遇到的所有错误的数组。 这个函数会给你详细的你遇到的最后一个错误。

 function Resolve-Error ($ErrorRecord=$Error[0]) { $ErrorRecord | Format-List * -Force $ErrorRecord.InvocationInfo |Format-List * $Exception = $ErrorRecord.Exception for ($i = 0; $Exception; $i++, ($Exception = $Exception.InnerException)) { "$i" * 80 $Exception |Format-List * -Force } } 

Powershell 3.0将一个ScriptStackTrace属性添加到ErrorRecord对象。 我使用这个函数进行错误报告:

 function Write-Callstack([System.Management.Automation.ErrorRecord]$ErrorRecord=$null, [int]$Skip=1) { Write-Host # blank line if ($ErrorRecord) { Write-Host -ForegroundColor Red "$ErrorRecord $($ErrorRecord.InvocationInfo.PositionMessage)" if ($ErrorRecord.Exception) { Write-Host -ForegroundColor Red $ErrorRecord.Exception } if ((Get-Member -InputObject $ErrorRecord -Name ScriptStackTrace) -ne $null) { #PS 3.0 has a stack trace on the ErrorRecord; if we have it, use it & skip the manual stack trace below Write-Host -ForegroundColor Red $ErrorRecord.ScriptStackTrace return } } Get-PSCallStack | Select -Skip $Skip | % { Write-Host -ForegroundColor Yellow -NoNewLine "! " Write-Host -ForegroundColor Red $_.Command $_.Location $(if ($_.Arguments.Length -le 80) { $_.Arguments }) } } 

Skip参数允许我离开Write-Callstack或Get-PSCallstack列表中的任意数量的error handling堆栈帧。

请注意,如果从catch块中调用,Get-PSCallstack将会丢失throw站点和catch块之间的任何帧。 因此,我更喜欢PS 3.0的方法,即使我们每帧的细节较less。

您不能从脚本的PowerShell代码exception中获取堆栈跟踪,只能从.NET对象中获取堆栈跟踪。 要做到这一点,你需要像下面这样获取Exception对象:

 $Error[0].Exception.StackTrace $Error[0].Exception.InnerException.StackTrace $Error[0].StackTrace 

我把我在这里find的东西作为灵感,创造了一个很好的function,任何人都可以放入代码中。

这就是我所说的:Write-Host“无法写入日志文件`n $(Resolve-Error)”-ForegroundColor Red

 Function Resolve-Error { <# .SYNOPSIS Enumerate error record details. .DESCRIPTION Enumerate an error record, or a collection of error record, properties. By default, the details for the last error will be enumerated. .PARAMETER ErrorRecord The error record to resolve. The default error record is the lastest one: $global:Error[0]. This parameter will also accept an array of error records. .PARAMETER Property The list of properties to display from the error record. Use "*" to display all properties. Default list of error properties is: Message, FullyQualifiedErrorId, ScriptStackTrace, PositionMessage, InnerException Below is a list of all of the possible available properties on the error record: Error Record: Error Invocation: Error Exception: Error Inner Exception(s): $_ $_.InvocationInfo $_.Exception $_.Exception.InnerException ------------- ----------------- ---------------- --------------------------- writeErrorStream MyCommand ErrorRecord Data PSMessageDetails BoundParameters ItemName HelpLink Exception UnboundArguments SessionStateCategory HResult TargetObject ScriptLineNumber StackTrace InnerException CategoryInfo OffsetInLine WasThrownFromThrowStatement Message FullyQualifiedErrorId HistoryId Message Source ErrorDetails ScriptName Data StackTrace InvocationInfo Line InnerException TargetSite ScriptStackTrace PositionMessage TargetSite PipelineIterationInfo PSScriptRoot HelpLink PSCommandPath Source InvocationName HResult PipelineLength PipelinePosition ExpectingInput CommandOrigin DisplayScriptPosition .PARAMETER GetErrorRecord Get error record details as represented by $_ Default is to display details. To skip details, specify -GetErrorRecord:$false .PARAMETER GetErrorInvocation Get error record invocation information as represented by $_.InvocationInfo Default is to display details. To skip details, specify -GetErrorInvocation:$false .PARAMETER GetErrorException Get error record exception details as represented by $_.Exception Default is to display details. To skip details, specify -GetErrorException:$false .PARAMETER GetErrorInnerException Get error record inner exception details as represented by $_.Exception.InnerException. Will retrieve all inner exceptions if there is more then one. Default is to display details. To skip details, specify -GetErrorInnerException:$false .EXAMPLE Resolve-Error Get the default error details for the last error .EXAMPLE Resolve-Error -ErrorRecord $global:Error[0,1] Get the default error details for the last two errors .EXAMPLE Resolve-Error -Property * Get all of the error details for the last error .EXAMPLE Resolve-Error -Property InnerException Get the "InnerException" for the last error .EXAMPLE Resolve-Error -GetErrorInvocation:$false Get the default error details for the last error but exclude the error invocation information .NOTES .LINK #> [CmdletBinding()] Param ( [Parameter(Mandatory=$false, Position=0, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] [ValidateNotNullorEmpty()] [array]$ErrorRecord, [Parameter(Mandatory=$false, Position=1)] [ValidateNotNullorEmpty()] [string[]]$Property = ('Message','InnerException','FullyQualifiedErrorId','ScriptStackTrace','PositionMessage'), [Parameter(Mandatory=$false, Position=2)] [switch]$GetErrorRecord = $true, [Parameter(Mandatory=$false, Position=3)] [switch]$GetErrorInvocation = $true, [Parameter(Mandatory=$false, Position=4)] [switch]$GetErrorException = $true, [Parameter(Mandatory=$false, Position=5)] [switch]$GetErrorInnerException = $true ) Begin { ## If function was called without specifying an error record, then choose the latest error that occured If (-not $ErrorRecord) { If ($global:Error.Count -eq 0) { # The `$Error collection is empty Return } Else { [array]$ErrorRecord = $global:Error[0] } } ## Define script block for selecting and filtering the properties on the error object [scriptblock]$SelectProperty = { Param ( [Parameter(Mandatory=$true)] [ValidateNotNullorEmpty()] $InputObject, [Parameter(Mandatory=$true)] [ValidateNotNullorEmpty()] [string[]]$Property ) [string[]]$ObjectProperty = $InputObject | Get-Member -MemberType *Property | Select-Object -ExpandProperty Name ForEach ($Prop in $Property) { If ($Prop -eq '*') { [string[]]$PropertySelection = $ObjectProperty Break } ElseIf ($ObjectProperty -contains $Prop) { [string[]]$PropertySelection += $Prop } } Write-Output $PropertySelection } # Initialize variables to avoid error if 'Set-StrictMode' is set $LogErrorRecordMsg = $null $LogErrorInvocationMsg = $null $LogErrorExceptionMsg = $null $LogErrorMessageTmp = $null $LogInnerMessage = $null } Process { ForEach ($ErrRecord in $ErrorRecord) { ## Capture Error Record If ($GetErrorRecord) { [string[]]$SelectedProperties = &$SelectProperty -InputObject $ErrRecord -Property $Property $LogErrorRecordMsg = $ErrRecord | Select-Object -Property $SelectedProperties } ## Error Invocation Information If ($GetErrorInvocation) { If ($ErrRecord.InvocationInfo) { [string[]]$SelectedProperties = &$SelectProperty -InputObject $ErrRecord.InvocationInfo -Property $Property $LogErrorInvocationMsg = $ErrRecord.InvocationInfo | Select-Object -Property $SelectedProperties } } ## Capture Error Exception If ($GetErrorException) { If ($ErrRecord.Exception) { [string[]]$SelectedProperties = &$SelectProperty -InputObject $ErrRecord.Exception -Property $Property $LogErrorExceptionMsg = $ErrRecord.Exception | Select-Object -Property $SelectedProperties } } ## Display properties in the correct order If ($Property -eq '*') { # If all properties were chosen for display, then arrange them in the order # the error object displays them by default. If ($LogErrorRecordMsg) {[array]$LogErrorMessageTmp += $LogErrorRecordMsg } If ($LogErrorInvocationMsg) {[array]$LogErrorMessageTmp += $LogErrorInvocationMsg} If ($LogErrorExceptionMsg) {[array]$LogErrorMessageTmp += $LogErrorExceptionMsg } } Else { # Display selected properties in our custom order If ($LogErrorExceptionMsg) {[array]$LogErrorMessageTmp += $LogErrorExceptionMsg } If ($LogErrorRecordMsg) {[array]$LogErrorMessageTmp += $LogErrorRecordMsg } If ($LogErrorInvocationMsg) {[array]$LogErrorMessageTmp += $LogErrorInvocationMsg} } If ($LogErrorMessageTmp) { $LogErrorMessage = 'Error Record:' $LogErrorMessage += "`n-------------" $LogErrorMsg = $LogErrorMessageTmp | Format-List | Out-String $LogErrorMessage += $LogErrorMsg } ## Capture Error Inner Exception(s) If ($GetErrorInnerException) { If ($ErrRecord.Exception -and $ErrRecord.Exception.InnerException) { $LogInnerMessage = 'Error Inner Exception(s):' $LogInnerMessage += "`n-------------------------" $ErrorInnerException = $ErrRecord.Exception.InnerException $Count = 0 While ($ErrorInnerException) { $InnerExceptionSeperator = '~' * 40 [string[]]$SelectedProperties = &$SelectProperty -InputObject $ErrorInnerException -Property $Property $LogErrorInnerExceptionMsg = $ErrorInnerException | Select-Object -Property $SelectedProperties | Format-List | Out-String If ($Count -gt 0) { $LogInnerMessage += $InnerExceptionSeperator } $LogInnerMessage += $LogErrorInnerExceptionMsg $Count++ $ErrorInnerException = $ErrorInnerException.InnerException } } } If ($LogErrorMessage) { $Output += $LogErrorMessage } If ($LogInnerMessage) { $Output += $LogInnerMessage } Write-Output $Output If (Test-Path -Path 'variable:Output' ) { Clear-Variable -Name Output } If (Test-Path -Path 'variable:LogErrorMessage' ) { Clear-Variable -Name LogErrorMessage } If (Test-Path -Path 'variable:LogInnerMessage' ) { Clear-Variable -Name LogInnerMessage } If (Test-Path -Path 'variable:LogErrorMessageTmp') { Clear-Variable -Name LogErrorMessageTmp } } } End {} } 

这是一个方法: 跟踪脚本堆栈

它的核心是这个代码:

     1..100 |  %{$ inv =&{gv -sc $ _ myinvocation}

您还可以更改错误对象的默认格式以包含堆栈跟踪。 基本上,通过从$ PSHOME \ PowerShellCore.format.ps1xml复制System.Management.Automation.ErrorRecord的块来创build你的格式文件,并添加你自己的添加跟踪的元素。 然后用Update-FormatData加载它。 有关更多详细信息,我刚刚写了一篇博客文章: https : //blogs.msdn.microsoft.com/sergey_babkins_blog/2016/12/28/getting-a-stack-trace-in-powershell/

哦,还有一件事:这不会自动传播到远程会话。 对象被格式化为远程端的string。 对于远程会话中的堆栈跟踪,您必须将该file upload到那里,然后再次调用Update-FormatData。

我只是想出来了。 catch块中捕获的$ _是exception。

 $errorString= $_ | Out-String