有没有办法在错误上输入调试器?

时间:2014-01-03 20:43:41

标签: debugging powershell powershell-v3.0

有没有办法进入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"它对我不起作用。陷阱通常似乎有一个类似的问题,它们突破了任何嵌套的范围,这是不希望的。

4 个答案:

答案 0 :(得分:26)

当然。您可以使用Set-PSBreakpoint cmdlet在PowerShell中创建条件断点。请考虑以下代码。将其另存为脚本文件,然后执行它。有一些内嵌评论可以帮助您了解正在发生的事情。

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

  • 可变
  • 命令

命令断点

此代码示例使用命令断点类型,因为我告诉它只在Get-WmiObject命令上设置断点。您可以交替指定特定的行号或变量断点类型。您可以使用-Action参数指定要在其下设置断点的条件。您必须在break -Action内的某处使用ScriptBlock关键字,以指示调试器暂停执行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,因此您可以在显示PSBreakpoint的行上设置Write-Error。以下是如何执行此操作的示例:

Set-PSBreakpoint -Command Write-Error -Action { break; };

很简单,对吧?

变量断点

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

  • 读写

代码:

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

# 2. Set a PSBreakpoint of type "variable" on a variable named "Data," but only when it has changed
Set-PSBreakpoint -Action { Write-Host -ForegroundColor Green -Object ('The $Data variable has changed! Value is: {0}' -f $Data); break; } -Variable Data -Mode Write;

# 3. No break on this line, because we are not changing the variable
Write-Host -Object $Data;

# 4. Execution is paused on this line, because we change the variable
$Data = 1;

线路断点

现在我们已经查看了变量命令 PSBreakpoint类型,要探索的最后一种断点是断点。如果您要复制/粘贴下面的代码,保存并执行它,您会看到Write-Host行上的代码中断(恰好是第9行),但仅限于Name属性$Service变量的值等于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);
}

答案 1 :(得分:19)

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

<#
.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
}

当第一个被调用时,实际上是在出错时进入调试器 启用。在写入错误StackTrace时会触发断点。

第二个函数在关闭错误时会进入调试器。

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


<强>更新

为了在没有配置文件功能的情况下使用这种技术,可以使用辅助脚本 Debug-Error.ps1 可以使用。理想情况下,它应该位于路径中,以便命令 <{1}}和Debug-Error始终可用。

另见blog post


Connect上的一些相关门票:

答案 2 :(得分:6)

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

function Debug-Here {
    if(!(Get-PSBreakpoint -Variable DebugHereCount)) {
        $SCRIPT:DebugHere= Set-PSBreakpoint -Variable DebugHereCount
    }
    $DebugHereCount++
}

然后从脚本顶部的陷阱调用该函数以捕获所有终止错误。

trap { Debug-Here }

或者从try-catch声明中调用它。

Get-Item DoesNotExist.txt -ErrorAction Stop

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

进入调试模式后,请检查$Error[0]以查看触发断点的内容。

答案 3 :(得分:2)

有点hokey但是在写错误或throw语句上工作:

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