Debugging 有没有办法在出现错误时进入调试器?

Debugging 有没有办法在出现错误时进入调试器?,debugging,powershell,powershell-3.0,Debugging,Powershell,Powershell 3.0,是否有方法输入PowerShell调试器以响应错误?ErrorAction参数有几个值,但我没有看到类似于Debug的值。我想要的是打开调试器,就像我设置了断点时一样,但只有在发生错误时才会打开调试器(例如,Write error) 编辑 我应该澄清一下:我主要是一名C#开发人员,对PowerShell有些陌生,我期望的是类似于Visual Studio调试器为“未处理的异常”提供的行为。PowerShell命令抛出异常似乎更为常见,而自定义脚本似乎主要使用Write Error。我不认为我特别

是否有方法输入PowerShell调试器以响应错误?
ErrorAction
参数有几个值,但我没有看到类似于
Debug
的值。我想要的是打开调试器,就像我设置了断点时一样,但只有在发生错误时才会打开调试器(例如,
Write error

编辑 我应该澄清一下:我主要是一名C#开发人员,对PowerShell有些陌生,我期望的是类似于Visual Studio调试器为“未处理的异常”提供的行为。PowerShell命令抛出异常似乎更为常见,而自定义脚本似乎主要使用Write Error。我不认为我特别想区分两者,但我想同时处理两者


Trevor Sullivan在下面的回答中提到,您可以使用
Set PSBreakpoint-Command Write Error-Action{break;}
似乎可以很好地捕获这些案例。尽管如此,我发现在许多情况下,它实际上是一个抛出异常的命令,我想打断它。如果设置
$ErrorActionPreference=“stop”
,Roman Kuzmin的答案似乎有效,但是,我有一个问题,我无法单步执行该程序,它似乎脱离了该位置并结束了脚本。如果
$ErrorActionPreference=“continue”
它对我不起作用。一般来说,陷阱似乎有一个类似的问题,即它们脱离了任何嵌套的作用域,这是不需要的。

当然。您可以使用
Set-PSBreakpoint
cmdlet在PowerShell中创建条件断点。考虑下面的代码。将其另存为脚本文件并执行。有在线评论可以帮助您了解发生了什么

请记住,有三种不同类型的断点:

  • 线
  • 变数
  • 命令
命令断点 这个代码示例使用命令断点类型,因为我告诉它只在
getwmiobject
命令上设置断点。您可以交替指定特定的行号或变量断点类型。您可以使用
-Action
参数指定设置断点的条件。您必须在
-Action
ScriptBlock
中的某个位置使用
break
关键字,以指示调试器暂停执行PowerShell脚本

# 1. Reset $Error to $null
$WmiError = $null;

# 2. Clean up any existing breakpoints
Get-PSBreakpoint | Remove-PSBreakpoint;

# 3. Set breakpoint, but only on Get-WmiObject commands, when the $WmiError variable is not $null
Set-PSBreakpoint -Command Get-WmiObject -Action { if ($WmiError) { break; } };

# 4. Failed Get-WmiObject command
Get-WmiObject -Class Win32_NonExistentClass -ErrorVariable WmiError;

# 5. Successful Get-WmiObject command
#    PowerShell breaks here, because:
#     - It's a Get-WmiObject command
#     - The $WmiError variable is not null
Get-WmiObject -Class Win32_BIOS;
由于您提到使用了
Write Error
,因此可以在出现
Write Error
的行上设置
PSBreakpoint
。以下是如何做到这一点的示例:

Set-PSBreakpoint -Command Write-Error -Action { break; };
很简单,对吧

可变断点 本例使用变量
PSBreakpoint
类型,但仅在修改变量内容时使用。您可以使用
-Mode
参数来确定在何种条件下命中变量断点:

  • 阅读
  • 读写
代码:

线断点 现在我们已经研究了变量命令
PSBreakpoint
类型,最后一种要研究的断点类型是断点。如果要复制/粘贴下面的代码,保存并执行它,您会看到代码在写入主机行(恰好是第9行)上中断,但只有当
$Service
变量的
Name
属性等于
WinRM
时才会中断。这就是
-Action
参数的
ScriptBlock
中的条件语句定义的内容

# 1. Clean up any existing breakpoints
Get-PSBreakpoint | Remove-PSBreakpoint;

# 2. Set a PSBreakpoint of type "line" on line #8, but only if the $Service variable's Name property equals 'winrm'
Set-PSBreakpoint -Action { if ($Service.Name -eq 'winrm') { break; } } -Line 9 -Script $MyInvocation.MyCommand.Path;

# 3. Get a list of Windows Services and iterate over them
foreach ($Service in (Get-WmiObject -Class Win32_Service)) {
    Write-Host -Object ('Service name is: {0}' -f $Service.Name);
}

您可以创建一个函数,在变量上设置断点,然后更改变量的值

function Debug-Here {
    if(!(Get-PSBreakpoint -Variable DebugHereCount)) {
        $SCRIPT:DebugHere= Set-PSBreakpoint -Variable DebugHereCount
    }
    $DebugHereCount++
}
然后从脚本顶部的陷阱a调用函数以捕获所有终止错误

trap { Debug-Here }
或者从
try catch
语句调用它

Get-Item DoesNotExist.txt -ErrorAction Stop

try {
    $x = 0
    $y = 1/$x
}
catch {
    Debug-Here
}

进入调试模式后,请检查
$Error[0]
以查看是什么触发了断点。

是的,有一种简单的方法可以在出现错误时进入调试器。看起来像 每次发生错误时,变量
StackTrace
都会更新。所以我 使用此技巧:在我的个人资料中,我有以下两个功能(开关):


功能SBP{
$null=设置PSBreakpoint-变量StackTrace-模式写入
}
功能限制性商业惯例{
获取PSBreakpoint-变量StackTrace |删除PSBreakpoint
}
当调用第一个时,在出现错误时闯入调试器实际上是错误的 启用。在出现错误时,写入堆栈跟踪,这将触发断点

第二个函数用于关闭在出错时中断调试器

在大多数情况下,这种方法对我很有效


更新

为了在不使用概要文件函数的情况下使用此技术,需要一个助手脚本,如 可以使用。理想情况下,它应该位于路径中,以便
调试错误
调试错误-关闭
始终可用

另见此


Connect上的一些相关票证:


有点古怪,但适用于写错误或抛出语句:

$error.clear()
$global:errcnt=0
Set-PSBreakpoint -Command * -action {if ($error.count -ne $global:errcnt) {$global:errcnt=$error.count;break}}

很好的问题-刚刚发布了一个答案,并附有示例。这很好。我想实现一个采用scriptblock参数的BreakIf函数。脚本决定是否接受断点。使用行断点时,BreakIf脚本必须知道要在其内部设置bp的行号。这给了我另一个想法:使用Get-PSCallStack在调用者中查找行号以设置中断。这样,中断会在中断后立即发生,而不是在中断内部。如果PowerShell ISE正确集成此功能,则情况仍会好得多。例如,在命中断点后,您必须手动运行步骤到以实际发生错误:此断点将返回到引发错误之前的时间!然而,这是可能的
<#
.Synopsis
    Sets $StackTrace breakpoint
.Link
    rbps
#>
function sbps {
    $null = Set-PSBreakpoint -Variable StackTrace -Mode Write
}

<#
.Synopsis
    Removes $StackTrace breakpoint
.Link
    sbps
#>
function rbps {
    Get-PSBreakpoint -Variable StackTrace | Remove-PSBreakpoint
}
$error.clear()
$global:errcnt=0
Set-PSBreakpoint -Command * -action {if ($error.count -ne $global:errcnt) {$global:errcnt=$error.count;break}}