关于 Debugger

简短说明

介绍 PowerShell 调试器。

详细说明

调试是在脚本运行时检查脚本以识别和更正脚本指令中的错误的过程。 PowerShell 调试器可以帮助您检查和识别脚本、函数、命令、PowerShell 工作流、PowerShell Desired State Configuration (DSC) 配置或表达式中的错误和低效情况。

从 PowerShell 5.0 开始,PowerShell 调试器已更新为调试在远程计算机上的控制台或 Windows PowerShell ISE 中运行的脚本、函数、工作流、命令、配置或表达式。 可以运行 Enter-PSSession 来启动交互式远程 PowerShell 会话,可以在其中设置断点并在远程计算机上调试脚本文件和命令。 Enter-PSSession 功能已更新,允许您重新连接并进入在远程计算机上运行脚本或命令的断开连接的会话。 如果正在运行的脚本命中断点,客户端会话会自动启动调试器。 如果正在运行脚本的断开连接的会话已经命中断点,并在断点处停止, Enter-PSSession 则在您重新连接到会话后自动启动命令行调试器。

PowerShell 调试器还可用于在 PowerShell 控制台或 Windows PowerShell ISE 中调试 PowerShell 工作流。 从 PowerShell 5.0 开始,您可以在本地或远程调试正在运行的作业或进程。

您可以使用 PowerShell 调试器的功能来检查 PowerShell 脚本、函数、命令、工作流或表达式,前提是该脚本、函数、命令、工作流或表达式正在运行。 PowerShell 调试器包括一组 cmdlet,可用于设置断点、管理断点和查看调用堆栈。

Debugger Cmdlets

PowerShell 调试器包括以下 cmdlet 集:

  • Set-PSBreakpoint:在行、变量和命令上设置断点。
  • Get-PSBreakpoint:获取当前会话中的断点。
  • Disable-PSBreakpoint:关闭当前会话中的断点。
  • Enable-PSBreakpoint:重新启用当前会话中的断点。
  • Remove-PSBreakpoint:从当前会话中删除断点。
  • Get-PSCallStack:显示当前调用堆栈。

启动和停止调试器

要启动调试器,请设置一个或多个断点。 然后,运行要调试的脚本、命令或函数。

到达断点时,执行将停止,并将控制切换到调试器。

要停止调试器,请运行脚本、命令或函数,直到它完成。 或者,键入 stopt

调试器命令

在 PowerShell 控制台中使用调试器时,请使用以下命令来控制执行。 在 Windows PowerShell ISE 中,使用“调试”菜单上的命令。

注: 有关如何在其他主机应用程序中使用调试器的信息,请参阅主机应用程序文档。

  • sStepInto:执行下一个语句,然后停止。

  • vStepOver:执行下一个语句,但跳过函数和调用。 将执行跳过的语句,但不会单步遍历。

  • Ctrl+Break:(在 ISE 中全部中断)在 PowerShell 控制台或 Windows PowerShell ISE 中闯入正在运行的脚本。 请注意,Windows PowerShell 2.0、3.0 和 4.0 中的 Ctrl+中断 关闭程序。 中断所有作同时适用于本地和远程交互式运行的脚本。

  • oStepOut:走出当前函数;如果嵌套,则上升一个级别。 如果位于主体中,它将继续结束或下一个断点。 将执行跳过的语句,但不会单步遍历。

  • cContinue:继续运行,直到脚本完成或到达下一个断点为止。 将执行跳过的语句,但不会单步遍历。

  • lList:显示正在执行的脚本部分。 默认情况下,它显示当前行、5 个上一行和 10 个后续行。 若要继续列出脚本,请按 Enter。

  • l <m>List:显示脚本的 16 行,以 <m>指定的行号开头。

  • l <m> <n>List:显示脚本的 <n> 行,以 <m>指定的行号开头。

  • qStopExit:停止执行脚本并退出调试器。 如果要通过运行 Debug-Job cmdlet 来调试作业,Exit 命令会分离调试器,并允许作业继续运行。

  • kGet-PsCallStack:显示当前调用堆栈。

  • <Enter>:如果最后一个命令是 Step (s)、StepOver (v) 或 List (l),则重复最后一个命令。 否则,表示提交作。

  • ?h:显示调试器命令帮助。

要退出调试器,您可以使用 Stop (q)。

从 PowerShell 5.0 开始,可以运行 Exit 命令退出通过运行 Debug-JobDebug-Runspace启动的嵌套调试会话。

通过使用这些调试器命令,您可以运行脚本、在关注点上停止、检查变量的值和系统状态,并继续运行脚本,直到发现问题。

注意:如果单步执行带有重定向运算符(如 “”)>的语句,则 PowerShell 调试器将单步执行脚本中所有剩余的语句。

显示脚本变量的值

在调试器中时,还可以输入命令、显示变量值、使用 cmdlet 并在命令行中运行脚本。

您可以显示正在调试的脚本中所有变量的当前值,但以下自动变量除外:

$_
$Args
$Input
$MyInvocation
$PSBoundParameters

如果尝试显示其中任一变量的值,则可以在调试器使用的内部管道中获取该变量的值,而不是脚本中变量的值。

要显示正在调试的脚本的这些变量的值,请在脚本中将 automatic 变量的值分配给新变量。 然后,可以显示新变量的值。

例如,

$scriptArgs = $Args
$scriptArgs

在本主题的示例中,变量的值 $MyInvocation 将按如下方式重新分配:

$scriptname = $MyInvocation.MyCommand.Path

调试器环境

到达断点时,请输入调试器环境。 命令提示符会更改,使其以“[DBG]:”开头。 如果要调试工作流,则提示符为 “[WFDBG]”。 您可以自定义提示。

有关自定义提示的详细信息,请参阅 about_Prompts

此外,在某些主机应用程序(如 PowerShell 控制台)中(但在 Windows PowerShell 集成脚本环境 [ISE] 中不是),会打开一个嵌套提示进行调试。 可以通过在命令提示符处显示的重复大于字符(ASCII 62)来检测嵌套提示。

例如,以下是 PowerShell 控制台中的默认调试提示符:

[DBG]: PS (get-location)>>>

可以使用 $NestedPromptLevel 自动变量找到嵌套级别。

此外,在局部范围内定义了一个自动变量 $PSDebugContext。 您可以使用变量的存在 $PsDebugContext 来确定您是否在调试器中。

例如:

if ($PSDebugContext) {"Debugging"} else {"Not Debugging"}

可以在调试中使用 $PSDebugContext 变量的值。

[DBG]: PS>>> $PSDebugContext.InvocationInfo

Name   CommandLineParameters  UnboundArguments  Location
----   ---------------------  ----------------  --------
=      {}                     {}                C:\ps-test\vote.ps1 (1)

调试和范围

进入调试器不会更改您作的范围,但当您到达脚本中的断点时,您将进入脚本范围。 脚本范围是运行调试器的范围的子级。

若要查找脚本作用域中定义的变量和别名,请使用 Get-AliasGet-Variable cmdlet 的 Scope 参数。

例如,以下命令获取本地(脚本)范围内的变量:

Get-Variable -scope 0

可以将命令缩写为:

gv -s 0

这是一种仅查看脚本中定义的变量以及调试时定义的变量的有用方法。

在命令行中调试

设置变量断点或命令断点时,只能在脚本文件中设置断点。 但是,默认情况下,断点在当前会话中运行的任何内容上设置。

例如,如果在 $name 变量上设置断点,调试器将在运行的任何脚本、命令、函数、脚本 cmdlet 或表达式中的任何 $name 变量上中断,直到禁用或删除断点。

这样,就可以在更现实的上下文中调试脚本,这些脚本可能会受到会话和用户配置文件中的函数、变量和其他脚本的影响。

行断点特定于脚本文件,因此仅在脚本文件中设置它们。

调试工作流

PowerShell 4.0 调试器可用于在 PowerShell 控制台或 Windows PowerShell ISE 中调试 PowerShell 工作流。 使用 PowerShell 调试器调试工作流存在一些限制。

  • 您可以在调试器中查看工作流变量,但不支持从调试器中设置工作流变量。
  • 在工作流调试器中停止时,Tab 键完成不可用。
  • 工作流调试只适合用于从 PowerShell 脚本同步运行工作流。 如果工作流作为作业运行(使用 AsJob 参数),则无法调试工作流。
  • 其他嵌套调试方案(例如,调用其他工作流的工作流或调用脚本的工作流)未实现。

以下示例演示如何调试工作流。 当调试器单步执行工作流函数时,调试器提示符将更改为“[WFDBG]”。

PS C:> Set-PSBreakpoint -Script C:\TestWFDemo1.ps1 -Line 8

ID Script           Line Command    Variable     Action
-- ------           ---- -------    --------     ------
0 TestWFDemo1.ps1   8

PS C:> C:\TestWFDemo1.ps1
Entering debug mode. Use h or ? for help.

Hit Line breakpoint on 'C:\TestWFDemo1.ps1:8'

At C:\TestWFDemo1.ps1:8 char:5
+     Write-Output -InputObject "Now writing output:"
# +!INCLUDE[]~~~~~

[WFDBG:localhost]: PS C:>> list

# 3:

4:  workflow SampleWorkflowTest
5:  {
6:      param ($MyOutput)
# 7:

8:*     Write-Output -InputObject "Now writing output:"
9:      Write-Output -Input $MyOutput
# 10:

11:      Write-Output -InputObject "Get PowerShell process:"
12:      Get-Process -Name powershell
# 13:

14:      Write-Output -InputObject "Workflow function complete."
15:  }
# 16:

17:  # Call workflow function
18:  SampleWorkflowTest -MyOutput "Hello"

[WFDBG:localhost]: PS C:>> $MyOutput
Hello
[WFDBG:localhost]: PS C:>> stepOver
Now writing output:
At C:\TestWFDemo1.ps1:9 char:5
+     Write-Output -Input $MyOutput
# +!INCLUDE[]~

[WFDBG:localhost]: PS C:>> list

4:  workflow SampleWorkflowTest
5:  {
6:      param ($MyOutput)
# 7:

8:      Write-Output -InputObject "Now writing output:"
9:*     Write-Output -Input $MyOutput
# 10:

11:      Write-Output -InputObject "Get PowerShell process:"
12:      Get-Process -Name powershell
# 13:

14:      Write-Output -InputObject "Workflow function complete."
15:  }
# 16:

17:  # Call workflow function
18:  SampleWorkflowTest -MyOutput "Hello"
# 19:


[WFDBG:localhost]: PS C:>> stepOver
Hello
At C:\TestWFDemo1.ps1:11 char:5
+     Write-Output -InputObject "Get PowerShell process:"
# +!INCLUDE[]~~~~~~~~~

[WFDBG:localhost]: PS C:>> stepOut
Get PowerShell process:

Handles  NPM(K)    PM(K)    WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----    ----- -----   ------     -- -----------
    433      35   106688   128392   726     2.67   7124 powershell
    499      44   134244   172096   787     2.79   7452 powershell

Workflow function complete.

调试函数

在具有 BeginProcessEnd 节的函数上设置断点时,调试器将在每个部分的第一行中断。

例如:

function test-cmdlet {
    begin {
        write-output "Begin"
    }
    process {
        write-output "Process"
    }
    end {
        write-output "End"
    }
}

C:\PS> Set-PSBreakpoint -command test-cmdlet

C:\PS> test-cmdlet

Begin
Entering debug mode. Use h or ? for help.

Hit Command breakpoint on 'prompt:test-cmdlet'

test-cmdlet

[DBG]: C:\PS> c
Process
Entering debug mode. Use h or ? for help.

Hit Command breakpoint on 'prompt:test-cmdlet'

test-cmdlet

[DBG]: C:\PS> c
End
Entering debug mode. Use h or ? for help.

Hit Command breakpoint on 'prompt:test-cmdlet'

test-cmdlet

# [DBG]: C:\PS>

调试远程脚本

从 PowerShell 5.0 开始,您可以在远程会话中、控制台或 Windows PowerShell ISE 中运行 PowerShell 调试器。 Enter-PSSession 功能已更新,允许您重新连接并进入在远程计算机上运行且当前正在运行脚本的已断开连接的会话。 如果正在运行的脚本命中断点,客户端会话会自动启动调试器。

以下示例显示了其工作原理,在脚本中的第 6、11、22 和 25 行设置了断点。 请注意,在该示例中,当调试器启动时,有两个标识提示:运行会话的计算机的名称,以及让您知道处于调试模式的 DBG 提示。

Enter-Pssession -Cn localhost
[localhost]: PS C:\psscripts> Set-PSBreakpoint .\ttest19.ps1 6,11,22,25

ID Script          Line     Command          Variable          Action
-- ------          ----     -------          --------          ------
0 ttest19.ps1          6
1 ttest19.ps1          11
2 ttest19.ps1          22
3 ttest19.ps1          25

[localhost]: PS C:\psscripts> .\ttest19.ps1
Hit Line breakpoint on 'C:\psscripts\ttest19.ps1:11'

At C:\psscripts\ttest19.ps1:11 char:1
+ $winRMName = "WinRM"
# + ~

[localhost]: [DBG]: PS C:\psscripts>> list

6:      1..5 | foreach { sleep 1; Write-Output "hello2day $_" }
7:  }
# 8:

9:  $count = 10
10:  $psName = "PowerShell"
11:* $winRMName = "WinRM"
12:  $myVar = 102
# 13:

14:  for ($i=0; $i -lt $count; $i++)
15:  {
16:      sleep 1
17:      Write-Output "Loop iteration is: $i"
18:      Write-Output "MyVar is $myVar"
# 19:

20:      hello2day
# 21:


[localhost]: [DBG]: PS C:\psscripts>> stepover
At C:\psscripts\ttest19.ps1:12 char:1
+ $myVar = 102
# + ~

[localhost]: [DBG]: PS C:\psscripts>> quit
[localhost]: PS C:\psscripts> Exit-PSSession
PS C:\psscripts>

例子

此测试脚本检测作系统的版本并显示适合系统的消息。 它包括函数、函数调用和变量。

以下命令显示测试脚本文件的内容:

PS C:\PS-test>  Get-Content test.ps1

function psversion {
  "PowerShell " + $PSVersionTable.PSVersion
  if ($PSVersionTable.PSVersion.Major -lt 6) {
    "Upgrade to PowerShell 6.0!"
  }
  else {
    "Have you run a background job today (start-job)?"
  }
}

$scriptName = $MyInvocation.MyCommand.Path
psversion
"Done $scriptName."

若要开始,请在脚本感兴趣的点设置断点,例如行、命令、变量或函数。

首先,在当前目录中 Test.ps1 脚本的第一行上创建一个行断点。

PS C:\ps-test> Set-PSBreakpoint -line 1 -script test.ps1

可以将此命令缩写为:

PS C:\ps-test> spb 1 -s test.ps1

该命令返回一个行断点对象 (System.Management.Automation.LineBreakpoint)。

Column     : 0
Line       : 1
Action     :
Enabled    : True
HitCount   : 0
Id         : 0
Script     : C:\ps-test\test.ps1
ScriptName : C:\ps-test\test.ps1

现在,启动脚本。

PS C:\ps-test> .\test.ps1

当脚本到达第一个断点时,断点消息指示调试器处于活动状态。 它描述断点并预览脚本的第一行,这是函数声明。 命令提示符也会更改,以指示调试器具有控制权。

预览行包括脚本名称和预览命令的行号。

Entering debug mode. Use h or ? for help.

Hit Line breakpoint on 'C:\ps-test\test.ps1:1'

test.ps1:1   function psversion {
# DBG>

使用 Step 命令(s)执行脚本中的第一个语句并预览下一个语句。 下一语句使用 $MyInvocation 自动变量将 $scriptName 变量的值设置为脚本文件的路径和文件名。

DBG> s
test.ps1:11  $scriptName = $MyInvocation.MyCommand.Path

此时, $scriptName 不会填充变量,但您可以通过显示变量的值来验证变量的值。 在本例中,该值为 $null

DBG> $scriptname
# DBG>

使用另一个 Step 命令执行当前语句并预览脚本中的下一个语句。 下一个语句调用 PsVersion 函数。

DBG> s
test.ps1:12  psversion

此时,将填充 $scriptName 变量,但通过显示变量的值来验证变量的值。 在这种情况下,该值设置为脚本路径。

DBG> $scriptName
C:\ps-test\test.ps1

使用另一个 Step 命令执行函数调用。 按 Enter,或键入“s”进行步骤。

DBG> s
test.ps1:2       "PowerShell " + $PSVersionTable.PSVersion

调试消息包括函数中语句的预览。 若要执行此语句并预览函数中的下一个语句,可以使用 Step 命令。 但是,在这种情况下,请使用 StepOut 命令(o)。 它完成函数的执行(除非它到达断点),并执行脚本中的下一个语句。

DBG> o
Windows PowerShell 2.0
Have you run a background job today (start-job)?
test.ps1:13  "Done $scriptName"

由于我们位于脚本中的最后一个语句上,因此 Step、StepOut 和 Continue 命令具有相同的效果。 在这种情况下,请使用 StepOut (o)。

Done C:\ps-test\test.ps1
PS C:\ps-test>

StepOut 命令执行最后一个命令。 标准命令提示符指示调试器已退出并返回对命令处理器的控制。

现在,再次运行调试器。 首先,若要删除当前断点,请使用 Get-PSBreakpointRemove-PSBreakpoint cmdlet。 (如果你认为你可能重复使用断点,请使用 Disable-PSBreakpoint cmdlet 而不是 Remove-PSBreakpoint。)

PS C:\ps-test> Get-PSBreakpoint| Remove-PSBreakpoint

可以将此命令缩写为:

PS C:\ps-test> gbp | rbp

或者,通过编写函数来运行命令,例如以下函数:

function delbr { gbp | rbp }

现在,在 $scriptname 变量上创建断点。

PS C:\ps-test> Set-PSBreakpoint -variable scriptname -script test.ps1

可以将命令缩写为:

PS C:\ps-test> sbp -v scriptname -s test.ps1

现在,启动脚本。 脚本到达变量断点。 默认模式为 Write,因此执行会在更改变量值的语句之前停止。

PS C:\ps-test> .\test.ps1
Hit Variable breakpoint on 'C:\ps-test\test.ps1:$scriptName'
(Write access)

test.ps1:11  $scriptName = $MyInvocation.MyCommand.Path
# DBG>

显示 $scriptName$null 变量的当前值。

DBG> $scriptName
# DBG>

使用 Step 命令执行填充变量的语句。 然后,显示 $scriptName 变量的新值。

DBG> $scriptName
C:\ps-test\test.ps1
```powershell

Use a Step command (s) to preview the next statement in the script.

```powershell
DBG> s
test.ps1:12  psversion

下一个语句是对 PsVersion 函数的调用。 要跳过该函数但仍执行它,请使用 StepOver 命令 (v)。 如果您在使用 StepOver 时已经在函数中,则它无效。 将显示函数调用,但不会执行该调用。

DBG> v
Windows PowerShell 2.0
Have you run a background job today (start-job)?
test.ps1:13  "Done $scriptName"

StepOver 命令执行该函数,并预览脚本中的下一条语句,该语句将打印最后一行。

使用 Stop 命令 (t) 退出调试器。 命令提示符将还原为标准命令提示符。

C:\ps-test>

若要删除断点,请使用 Get-PSBreakpointRemove-PSBreakpoint cmdlet。

PS C:\ps-test> Get-PSBreakpoint| Remove-PSBreakpoint

在 PsVersion 函数上创建新的命令断点。

PS C:\ps-test> Set-PSBreakpoint -command psversion -script test.ps1

可以缩写此命令来:

PS C:\ps-test> sbp -c psversion -s test.ps1

现在,运行脚本。

PS C:\ps-test> .\test.ps1
Hit Command breakpoint on 'C:\ps-test\test.ps1:psversion'

test.ps1:12  psversion
# DBG>

脚本在函数调用时到达断点。 此时,该函数尚未被调用。 这使你有机会使用 Set-PSBreakpoint 的作参数来设置断点执行的条件或执行准备或诊断任务,例如启动日志或调用诊断或安全脚本。

若要设置作,请使用 Continue 命令 (c) 退出脚本,并使用 Remove-PSBreakpoint 命令删除当前断点。 (断点是只读的,因此无法向当前断点添加作。

DBG> c
Windows PowerShell 2.0
Have you run a background job today (start-job)?
Done C:\ps-test\test.ps1

PS C:\ps-test> Get-PSBreakpoint| Remove-PSBreakpoint
PS C:\ps-test>

现在,使用作创建新的命令断点。 以下命令设置一个命令断点,其中包含一个作,用于在调用函数时记录 $scriptName 变量的值。 由于作中未使用 Break 关键字,因此执行不会停止。 (反引号 (') 是行继续符。

PS C:\ps-test> Set-PSBreakpoint -command psversion -script test.ps1  `
-action { add-content "The value of `$scriptName is $scriptName." `
-path action.log}

还可以添加设置断点条件的作。 在以下命令中,仅当执行策略设置为 RemoteSigned(仍允许运行脚本的最严格的策略)时,才会执行命令断点。 (反引号 (') 是延续字符。

PS C:\ps-test> Set-PSBreakpoint -script test.ps1 -command psversion `
-action { if ((Get-ExecutionPolicy) -eq "RemoteSigned") { break }}

作中的 Break 关键字指示调试器执行断点。 您还可以使用 Continue 关键字指示调试器在不中断的情况下执行。 由于默认关键字是 Continue,因此必须指定 Break 才能停止执行。

现在,运行脚本。

PS C:\ps-test> .\test.ps1
Hit Command breakpoint on 'C:\ps-test\test.ps1:psversion'

test.ps1:12  psversion

由于执行策略设置为 RemoteSigned,因此执行会在函数调用处停止。

此时,可能需要检查调用堆栈。 Get-PsCallStack使用 cmdlet 或 Get-PsCallStack debugger 命令 (k)。 以下命令获取当前调用堆栈。

DBG> k
2: prompt
1: .\test.ps1: $args=[]
0: prompt: $args=[]

此示例仅演示了使用 PowerShell 调试器的几种方法。

有关调试器 cmdlet 的详细信息,请键入以下命令:

help <cmdlet-name> -full

例如,键入:

help Set-PSBreakpoint -full

PowerShell 中的其他调试功能

除了 PowerShell 调试器之外,PowerShell 还包括可用于调试脚本和函数的其他几个功能。

  • Windows PowerShell ISE 包括一个交互式图形调试器。 有关详细信息,请启动 Windows PowerShell ISE 并按 F1。

  • Set-PSDebug cmdlet 提供非常基本的脚本调试功能,包括单步执行和跟踪。

  • Set-StrictMode使用 cmdlet 可检测对未初始化变量的引用、对对象不存在的属性的引用以及无效的函数语法。

  • 将诊断语句添加到脚本,例如显示变量值的语句、从命令行读取输入的语句或报告当前指令的语句。 使用包含此任务的 Write 谓词的 cmdlet,例如 Write-HostWrite-DebugWrite-WarningWrite-Verbose

另请参阅