关于调试器

简短说明

介绍 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,可用于设置断点、管理断点和查看调用堆栈。

调试器 Cmdlet

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 PowerShell2.0、3.0 和 4.0 中的 Ctrl +Break 将关闭程序。 “全部中断”适用于本地和远程交互运行的脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

显示脚本变量的值

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

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

$_
$Args
$Input
$MyInvocation
$PSBoundParameters

如果你尝试显示这些变量中的任何一个的值,你将获取调试器使用的内部管道中变量的值,而不是脚本中变量的值。

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

例如,

$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-Variable cmdlet 的 Get-Alias Scope 参数。

例如,以下命令获取局部 (脚本中的变量) 范围:

Get-Variable -scope 0

可以将命令缩写为:

gv -s 0

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

在命令行上进行调试

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

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

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

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

调试工作流

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

  • 可以在调试器中查看工作流变量,但不支持从调试器中设置工作流变量。
  • 在工作流调试器中停止时选项卡完成不可用。
  • 工作流调试仅适用于从 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 命令 () 执行脚本中的第一个语句并预览下一个语句。 下一个语句使用 $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 命令执行 函数,并预览脚本中的下一个语句,该语句输出最后一行。

使用停止命令 (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 Action 参数设置断点执行的条件,或者执行准备或诊断任务,例如启动日志或调用诊断或安全脚本。

若要设置操作,请使用继续命令 (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 调试器命令 (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。

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

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

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

另请参阅