The Windows PowerShell Debugger

The information in this article was written against the Community Technology Preview (CTP) of Windows PowerShell 2.0. This information is subject to change in future releases of Windows PowerShell 2.0.

On This Page

The PowerShell Debugger Setting Breakpoints Responding to Breakpoints Listing Breakpoints Enabling and Disabling Breakpoints Removing Breakpoints

The PowerShell Debugger

In Windows PowerShell 2.0 (the November 2007 Community Technology Preview release) the PowerShell team has taken an interesting approach to script debugging. As you know, PowerShell doesn’t require a specialized script editor or development environment. Instead, PowerShell users can, and do, use any and all text editors (from Notepad on up) to write their scripts. Because of that, the PowerShell team decided to build their debugging tools into Windows PowerShell itself; in turn, that means that you can use the new debugging cmdlets to debug any script from the console window itself.

Pretty cool, huh?

But don’t just take our word for that; let’s show you how some of these cmdlets work. In particular, let’s take a brief look at the following new PowerShell 2.0 cmdlets:

  • New-PsBreakpoint

  • Get-PsBreakpoint

  • Enable-PsBreakpoint

  • Disable-PsBreakpoint

  • Remove-PsBreakpoint

  • Step-Into

  • Step-Out

Setting Breakpoints

Windows PowerShell’s new debugging features are built around the notion of “breakpoints.” A breakpoint is simply a designated spot in a script where you would like execution to pause. For example, suppose you have a script that copies a file from one location to another, and then deletes the original file. (OK, admittedly, you’d be better off writing a script that simply moved the file, but that wouldn’t help us make our point.) Let’s further suppose that your script looks like this:

cls
Write-Host "Copying folder."
Copy-Item D:\Logfiles -destination D:\Backup
Write-Host "Deleting folder."
Remove-Item D:\Logfiles

As you might expect, this script hinges on one key line of code: the line where the Copy-Item cmdlet copies the folder D:\Logfiles to D:\Backup. What makes this line so crucial? Well, suppose this line fails but the script continues to run. Let’s further suppose that the last line of code, the one where the Remove-Item cmdlet deletes the original folder, succeeds. What would that mean? That would mean that the script failed to copy D:\Logfiles to the backup location, but succeeded in deleting D:\Logfiles (even though no backup copy exists). And that would mean that the folder D:\Logfiles, and everything in it, would be gone without a trace.

Probably not what you had in mind.

So what can you do about that? How can you test this script risk-free, or at least as close to risk-free as you can get?

Well, one thing you can do is set a breakpoint on line 4 (Write-Host "Deleting folder.").That enables you to run the script and execute lines 1, 2, and 3. When you get to line 4, however, the script will pause and wait for further instructions. (What kind of instructions? We’ll discuss that in a minute.) That, in turn, gives you a chance to verify that D:\Logfiles has been successfully copied to D:\Backup. If it has then you can continue to run the script. If it hasn’t, then you can type {break} to stop execution of the script before the folder is deleted. And once the script has successfully – and safely – been stopped, you can begin debugging lines 1 through 3 to try and determine why the folder didn’t get copied over.

That sounds pretty handy, doesn’t it? OK, so then how do you set a breakpoint on line 4? Why, by doing this, of course:

New-PSBreakpoint -script C:\Scripts\Test.ps1 -line 4

That was easy, wasn’t it? As you can see, all we had to do was call the New-PSBreakpoint cmdlet, passing New-PSBreakpoint two parameters:

  • -script, followed by the path to the script we want to debug.

  • -line, followed by the line number (4) where we want to place our breakpoint.

So what happens now? Well, now we simply run the script; when the script reaches line 4 it will pause and prompt us for further instructions. That scenario will play out something similar to this:

Copying folder.
DEBUG: Hit breakpoint(s) on 'C:\Scripts\Test.ps1:4'
DEBUG:  Line breakpoint on 'C:\Scripts\Test.ps1:4'
PS C:\scripts>>>{break}
PS C:\scripts>

Notice at the prompt that we typed {break} to terminate the script.

Important. As you might know (but probably didn’t), breakpoints are tied to the current PowerShell session and not to the script. That means that any breakpoints you set will disappear as soon as you exit PowerShell. Keep in mind, too, that the breakpoints work only in the PowerShell session where they were set. Suppose you create a new breakpoint in one PowerShell session and then open a second PowerShell session. That breakpoint will not be available in the second PowerShell session, you’ll need to reset any breakpoints you want in this second session.

Admittedly, setting a breakpoint on line 4 was pretty cool. But you ain’t seen nothin’ yet. Sure, it’s easy to set a breakpoint on a particular line in a script. (And yes, you can set as many breakpoints on a script as you want. On top of that, you can set breakpoints on as many different scripts as you want.) But PowerShell doesn’t limit you to setting breakpoints only on lines. Instead, you can also set breakpoints on such things as:

Variables. When you set a breakpoint on a variable the script will (by default) pause any time the value of that variable changes. To set a breakpoint on a variable, simply use the –variables parameter followed by the name (or names) of the variable of interest. (Just make sure to leave the $ off when specifying the variable name.) For example, this command sets a breakpoint on the variable $a:

New-PSBreakpoint -script C:\Scripts\Test.ps1 -variables a

As we noted, by default breakpoints are triggered any time the value of a variable changes (WriteMode). Alternatively, you could have a breakpoint triggered any time a variable value is read (Read); this includes each and every time that the value of this variable is displayed onscreen or used in a calculation. Or, set the breakpoint to ReadWriteMode and have the breakpoint triggered any time the variable is referenced.

Oh, good question. Here’s how you set the mode for a variable breakpoint:

New-PSBreakpoint -script C:\Scripts\Test.ps1 -variables a -Read

Commands. You can also set a breakpoint any time a particular command is used in a script. For example, the following command sets a breakpoint on the Get-Content command:

New-PSBreakpoint -script C:\Scripts\Test.ps1 -commands "Get-Content"

You can even get more specific than that. For example, this command sets a breakpoint on Get-Content, but only when the cmdlet is used to open the file C:\Scripts\Test.txt:

New-PSBreakpoint -script C:\Scripts\Test.ps1 -commands "Get-Content C:\Scripts\Test.txt"

Functions. Set a breakpoint any time a function is called? Hey, why not? Here’s a command that sets a breakpoint any time the function ConvertDate is called:

New-PSBreakpoint -script C:\Scripts\Test.ps1 -function ConvertDate

Before we move on, here’s one last note about the New-PSBreakpoint cmdlet. By default, the script simply pauses and waits for further instructions any time it encounters a breakpoint. If you want to, however, you can execute a specific command (or set of commands) when a breakpoint is encountered. To do that, simply add the –action parameter followed by the command or commands to be run. (Technically these commands need to be passed as a script block, which means they must be enclosed in curly braces.) For example, this command displays the value of the variable $a any time the specified breakpoint is triggered:

New-PSBreakpoint -script C:\Scripts\Test.ps1 -variables a -action {Write-Host $a}

One action you might want to specify is this: {break}. This will automatically terminate the script when a breakpoint is reached.

Responding to Breakpoints

Unless you use the –action parameter, any time you a hit a breakpoint the script will pause and present you with a nested command prompt. When that happens, PowerShell will simply sit patiently and wait for you to tell it what to do.

That’s great, except for one thing: what exactly can you tell it to do? To be honest, you can tell it pretty much anything you want to tell it. As we’ve already seen, you can simply type the keyword {break} and press ENTER; that will cause the script to terminate. Alternatively, you might want to run a full-fledged PowerShell command. For example, suppose your script is supposed to create a text file named C:\Scripts\Test.txt and then hit a breakpoint. At that point, you could use the Get-Content cmdlet to read the contents of that file:

Get-Content C:\Scripts\Test.txt

That’s pretty cool. However, more often than not you’ll end up executing one of the following new cmdlets any time you encounter a breakpoint:

Step-Into. The Step-Into cmdlet enables you to execute the next line of code in the script. Just type Step-Into at the command prompt and press ENTER; in response, PowerShell will execute the next line of code. At that point the script will stop and wait for further instructions, even if no breakpoint has been set on that particular line of code.

In other words, Step-Into allows you to run a script line-by-line.

Step-Out. When you call the Step-Out cmdlet your script will begin to run again, not stopping until it reaches the next breakpoint (or until it runs out of lines to execute). Unlike Step-Into, Step-Out does not run a script line-by-line. Instead, it runs until it reaches a breakpoint; pauses; runs until it reaches the next breakpoint; pauses; then – well, you get the idea.

To use this cmdlet, type Step-Out at the command prompt and then press ENTER; in response, PowerShell will execute the next line of code, and then continue to execute lines of code, without stopping, until the next breakpoint is encountered.

Important note. If you are inside a function when you call the Step-Out cmdlet, the debugger will exit the function and step to the statement immediately following the function call; from there the script will continue to run until the next breakpoint is encountered.

What does that mean? Well, suppose we are halfway through function A when we call Step-Out. Let’s further suppose that there are no more breakpoints in the script. In a case like that, the debugger will exit the function (without running any additional lines of code in that function), and then – because there are no more breakpoints –will run the rest of the script. What if there was another breakpoint? Then the script would stop at that breakpoint and await further instructions.

Step-Over. The Step-Over cmdlet is roughly similar to Step-Into: it’s designed to execute code line-by-line. However, there is an exception or two. (Which there should be; otherwise Step-Over would be Step-Into.) If the next line of code to be executed happens to be a function call, Step-Over will, well, “step over” that call. What does that mean? That means that Step-Over will execute the entire function without stopping; you will not step into the individual lines of code within that function. For example, suppose we hit a breakpoint at this point in a script:

Set-Location C:\Scripts
ConvertDate
Get-ChildItem

If we use the Step-Over cmdlet with these lines of code, the script will run line 1, the line that calls the Set-Location cmdlet, and then pause. If we use the Step-Over cmdlet again, the script will then run line 2, which calls a function named ConvertDate. At that point, all the lines of code within the Convert-Date function will execute, without stopping. The script will not pause until after the function has finished executing. If we call Step-Over a third time the script will then “step into” line 3, meaning it will pause on this line and await further instructions.

By the way, you can use any or all of these cmdlets during a single debugging session. That enables you to, say, step through a particular section line-by-line, and then step over or step out of the next section in the script.

Listing Breakpoints

On the one hand, it’s pretty cool to have breakpoints tied to the PowerShell environment rather than an individual script; on the other hand, that makes it harder to figure out which breakpoints, if any, have been placed on a script. (Why? Because you can’t just open up the script in a script editor or debugger and view the breakpoints.)That’s where the Get-PSBreakpoint cmdlet comes in. Called without any additional parameters Get-PSBreakpoint returns information similar to the following for all the breakpoints in the current PowerShell session:

Get-PSBreakpoint

Do that and you’ll get back information similar to this for each breakpoint set during the current session:

Function: ConvertDate
Action:
Enabled: True
HitCount: 1
Id: 0
Script: C:\Scripts
ScriptName: C:\Scripts\Test.ps1

Alternatively, you can specify a breakpoint ID and get back information only for the specified breakpoint:

Get-PSBreakpoint -ID 7

The –id parameter is the only parameter available to Get-PSBreakpoint. However, by piping the information retrieved by Get-PSBreakpoint to the Where-Object cmdlet you can retrieve a collection of breakpoints that fit some other criteria. For example, this command retrieves all the breakpoints associated with the script C:\Scripts\Test.ps1:

Get-PSBreakpoint | Where-Object (ScriptName - eq "C:\Scripts\Test.ps1")

Enabling and Disabling Breakpoints

By using the Enable-PSBreakpoint and Disable-PSBreakpoint cmdlets you can selectively enable and disable breakpoints during a Windows PowerShell session. To disable a breakpoint all you need to do is call the Disable-PSBreakpoint cmdlet, specifying the ID of the breakpoint to be disabled:

Disable-PSBreakpoint -ID 7

To enable that particular breakpoint, just use the Enable-Breakpoint cmdlet:

Enable-PSBreakpoint -ID 7

To disable (or enable) all the breakpoints in a PowerShell session, use Get-PSBreakpoint to retrieve a collection of breakpoints and then pipe that collection to the appropriate cmdlet. For example, this command disables all the breakpoints in the current session:

Get-PSBreakpoint | Disable-PSBreakpoint

Or, again, use a command like this to disable all the breakpoints associated with the script C:\Scripts\Test.ps1:

(Get-PSBreakpoint | Where-Object (ScriptName - eq "C:\Scripts\Test.ps1"))
| Disable-PSBreakpoint

Removing Breakpoints

As an alternative to disabling a breakpoint you can simply delete that breakpoint. Want to delete the breakpoint with the ID 7? Then simply call the Remove-PSBreakpoint cmdlet, specifying the breakpoint to be removed:

Remove-PSBreakpoint -ID 7

To delete all the breakpoints in the current PowerShell session us Get-PSBreakpoint to retrieve a collection of breakpoints and then pipe that collection to the Remove-PSBreakpoint cmdlet:

Get-PSBreakpoint | Remove-PSBreakpoint

And this command – oh, you guessed it. Yes, this command does remove all the breakpoints associated with the script C:\Scripts\Test.ps1:

(Get-PSBreakpoint | Where-Object (ScriptName - eq "C:\Scripts\Test.ps1"))
| Removeakpoint