Learn about Windows PowerShell
Summary: Learn how to use the Windows PowerShell script debugger to troubleshoot problems with scripts.
Microsoft Scripting Guy Ed Wilson here. One of the fun things about traveling, especially to warm places when it is winter back home, is calling to talk to friends and relatives. When they say things like, “It snowed yesterday,” I grimace a little and reluctantly tell them it was 70 degrees Fahrenheit (21 degrees Celsius according to my unit conversion module) in sunny Southern California. But the best thing is getting to work with customers and talking to people about Windows PowerShell.
Invariably, when I am talking to people about writing Windows PowerShell scripts, someone comes up with the question about script debugging. To be honest, I rarely fire up a debugger. Never have, even back in the VBScript days. I generally write code in such a way that when a problem occurs, it is obvious where the problem lies and how to correct it. Every once in a while, however, the problem is not obvious, and being able to actually debug the script comes in helpful.
In Windows PowerShell 2.0, we introduced several Windows PowerShell cmdlets that make it easier to debug scripts. Today, I want to spend a little time looking at some of the things to do with one of the cmdlets.
Debugging a Windows PowerShell script often involves setting a breakpoint, which is something that causes the Windows PowerShell script to pause execution. When the script pauses execution, the Windows PowerShell console drops into debug mode. This special mode permits the use of certain commands. The commands appear in the table that follows (this table is copied from my Microsoft Press book, Windows PowerShell 2.0 Best Practices book).
Executes the next statement and then stops.
Executes the next statement, but skips functions and invocations. The skipped statements are executed, but not stepped through.
Steps out of the current function up one level, if nested. If in the main body, it continues to the end or the next breakpoint. The skipped statements are executed, but not stepped through.
Continues to run until the script is complete or until the next breakpoint is reached. The skipped statements are executed, but not stepped through.
Displays the part of the script that is executing. By default, it displays the current line, five previous lines, and 10 subsequent lines. To continue listing the script, press Enter.
Displays 16 lines of the script beginning with the line number specified by <m>.
l <m> <n>
Displays <n> lines of the script, beginning with the line number specified by <m>.
Stops executing the script, and exits the debugger.
Displays the current call stack.
Repeats the last command if it was Step-into (s), Step-over (v), or List (l). Otherwise, represents a submit action.
h or ?
Displays the debugger command Help.
There are several ways to configure breakpoints. For the next several examples, I am going to use the script that is shown here:
$cn = "localhost"
$process = "notepad"
Get-WmiObject -Class win32_bios -cn $cn
Keep in mind that the path to the script matters. I am going to set a breakpoint on a command on a script that appears in a specific location. If I run the script with the same name from a different location, the script will not break upon that command.
When the command Get-Process cmdlet appears, the script breaks and enters debug mode. In the code that follows, I use the Set-PSBreakpoint cmdlet to set the breakpoint on the Get-Process cmdlet for the script c:\fso\mydebug.ps1. The code and associated output are shown here:
PS C:\> Set-PSBreakpoint -Command get-process -Script C:\fso\mydebug.ps1
ID Script Line Command Variable
-- ------ ---- ------- --------
0 mydebug.ps1 get-process
Now, I execute the Windows PowerShell script. When the script hits the command Get-Process, it does not execute the command. Instead, it enters the debugger. I now have access to the commands listed in the table above.
I type the letter L to list the portion of the script that executes. By default, this displays the current line, five previous lines, and 10 subsequent lines. After examining the code, I decide to continue with script execution, and I press the letter C to continue running the script. In this example, it runs the Get-Process cmdlet, and ends the script, because the Get-Process cmdlet is the last line in the script. The setting of the breakpoint, launching of the script, using the L and the C commands in the debugger, and execution of the last line in the script are shown in the following figure.
I next decide to use the Set-PSBreakpoint cmdlet to set another breakpoint. This time, I break on a variable. The key thing to remember here is that when specifying a variable as a breakpoint, do not include the $ prefix; instead, use the variable name without the dollar sign. The command is shown here:
Set-PSBreakpoint -script c:\fso\mydebug.ps1 -Variable cn
When the script reaches the cn variable, it enters the debugger. I change the value assigned to the cn variable from “localhost” to “mred” and use the C command to continue execution of the script. After the script executes a couple lines of code, the script enters the second breakpoint—the breakpoint I first set on the Get-Process command. Once again, the script enters the debugger, and once again I use the C command to continue script execution. These commands and associated output are shown in the following figure.
Now, I decide to see how many breakpoints I have set. I use the Get-PSBreakpoint cmdlet:
The command and associated output are shown here:
PS C:\> Get-PSBreakpoint
-- ------ ---- ------- --------
1 mydebug.ps1 cn
Now, I decide to get rid of all the breakpoints. To do this, I use the Get-PSBreakpoint cmdlet and pipe it to the Remove-PSBreakpoint cmdlet. Next, I use Get-PSBreakpoint to ensure I removed all the breakpoints. The commands and associated output are shown here:
PS C:\> Get-PSBreakpoint | Remove-PSBreakpoint
That is all there is to using a command to break into a script. Join me tomorrow when I will continue talking about debugging Windows PowerShell scripts.
I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at firstname.lastname@example.org, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy
even if I prefer to have a GUI like the ISE when debugging a script, it is really good to know that the debugging capabilities are not restricted to GUI hosts.
It is a bit more work to debug on the commandline, but it is a great feature that enables us to step through the script, examine the current context and inspect or change the state of the script on the fly.
I think it is worth learning and typing a bit!
[TYPO] "In the code that follows, I use the Set-PSBreakpoint cmdlet to set the breakpoint on the Get-Process cmdlet for the script c:\fso\psdebug.ps1."
It should be c:\fso\mydebug.ps1.
I think it is worth mentioning that you can also set a break point by line-number. In fact, this is the kind of breakpoint that will be set if you choose "Toggle Breakpoint" in the "Debug" menu of the ISE.
Set-PSBreakpoint -Line 3 -Script mydebug.ps1
In the ISE, this will highlight the line with a brown background, the same as pressing F9 when on the beginning of that line.
@Klaus Schulte Yes, you are right debugging scripts from the command line in the PowerShell console can be very productive. It takes a bit of effort, but can be powerful. Thank you for your comment.
@Aleksandar Nikolic Thank you for your close reading of the post. I have corrected my typo. In addition, the Scripting Wife also found another typo ... I have fixed that one as well.
@BigTeddy :-) I cover setting breakpoints by line number, and using the debugger in the ISE on Thursday. But you are absolutely correct ... about setting breakpoints by line number ... in fact, I know lots of people that seem to set a breakpoint on line 1, and then they step through the code to do their debugging. It is quick, and effecient, and it works.
I am having a problem following this blog entry.
I get an error when I execute the MyDebug.ps1 script even if I use the .\ format. The error is
Suggestion [3,General]: The command MyDebug.ps1 was not found, but does exist in the current location. Windows PowerShel
l doesn't load commands from the current location by default. If you trust this command, instead type ".\MyDebug.ps1". S
ee "get-help about_Command_Precedence" for more details.
It does open the Notepad window and does enter the debug mode if I have set that.
What am i doing wrong?
Please ignore my comment.
I was not familiar with your method of illustrating scripts and had included the MyDebug.ps1 as the first line. Doh!