Prof. Powershell
Reaching Your Breaking Point with PowerShell
Debug the longer PowerShell scripts by inserting breakpoints -- and some choice PowerShell cmdlets -- that will help you see how scripts are processing information along the way.
- By Jeffery Hicks
- 09/27/2011
When developing a PowerShell script, tracking down a bug is part of the job. Syntax bugs are generally easy because the script will fail to run and PowerShell will generate an error. But logic bugs are trickier as the script runs, but doesn't produce the result you expect. One way to track these types of bugs down is to use breakpoints.
A breakpoint is a marker you insert into a script that pauses or breaks script execution so that you can see what is happening in your script. Here's a simple sample script with a problem. I've added line numbers.
1. #requires -version 2.0
2.
3. Write-Host "I am starting" -ForegroundColor Green
4. $r=Read-Host "What is the radius maximum value?"
5. $pi=[math]::pi
6. $result=$pi*($r*$r)
7. Write $result
8. Write-Host "I am ending" -ForegroundColor Green
Since I'm not getting the result I expect, I'll insert a break point at line 6 so I can check things out using the Set-PSBreakpoint cmdlet.
PS C:\> Set-PSBreakpoint -Script c:\scripts\fixme.ps1 -Line 6
ID Script Line Command Variable Action
-- ------ ---- ------- -------- ------
1 fixme.ps1 6
Now I'm going to run the script and PowerShell will pause execution when it hits line 6 and drop me into a special Debug prompt.
PS C:\> c:\scripts\fixme.ps1
I am starting
What is the radius maximum value?: 5
Hit Line breakpoint on 'C:\scripts\fixme.ps1:6'
fixme.ps1:6 $result=$pi*($r*$r)
[DBG]: PS C:\>>>
At this prompt I can look at variables:
[DBG]: PS C:\>>> $pi
3.14159265358979
[DBG]: PS C:\>>> $r
5
[DBG]: PS C:\>>> $r*$r
55555
[DBG]: PS C:\>>>
Clearly $r is not what I think it is:
[DBG]: PS C:\>>> $r.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
Now I see why. By the way, you can also type ? at the prompt to get help for all the things you can do in the debug prompt. I'll let you experiment with the options.
But back to the problem at hand: Clearly $r is not getting treated as an integer. If you look at help for Read-Host you'll see that the cmdlet writes strings to the pipeline, which is what I'm seeing. But before I fix the problem, let me demonstrate another way to use Set-PSBreakpoint.
First I need to exit out of the debug prompt by typing exit and pressing return. Since there are no more break points, the script finishes. Use Get-PSBreakpoint to see all breakpoints in your session. Then use the Remove-PSBreakpoint cmdlet, which does just what the name implies:
PS C:\> get-psbreakpoint | Remove-PSBreakpoint
I can also set a breakpoint on a variable:
PS C:\> Set-PSBreakpoint -Script c:\scripts\fixme.ps1 -Variable r
ID Script Line Command Variable Action
-- ------ ---- ------- -------- ------
2 fixme.ps1 r
Now, PowerShell will break whenever the variable is accessed:
PS C:\> c:\scripts\fixme.ps1
I am starting
What is the radius maximum value?: 5
Hit Variable breakpoint on 'C:\scripts\fixme.ps1:$r' (Write access)
fixme.ps1:4 $r=Read-Host "What is the radius maximum value?"
[DBG]: PS C:\>>> list
1: #requires -version 2.0
2:
3: Write-Host "I am starting" -ForegroundColor Green
4:* $r=Read-Host "What is the radius maximum value?"
5: $pi=[math]::pi
6: $result=$pi*($r*$r)
7: Write $result
8: Write-Host "I am ending" -ForegroundColor Green
9:
[DBG]: PS C:\>>> c
174531.179870181
I am ending
PS C:\>
This is a useful technique to track variable changes, which doesn't really apply in my situation. But by breaking script execution I was able to peek into the pipeline and identify my problem. The fix, by the way, is to cast the variable as an integer:
[int]$r=Read-Host "What is the radius maximum value?"
Running the script through the breakpoint confirms this works:
PS C:\> c:\scripts\fixme.ps1
I am starting
What is the radius maximum value?: 5
Hit Variable breakpoint on 'C:\scripts\fixme.ps1:$r' (Write access)
fixme.ps1:4 [int]$r=Read-Host "What is the radius maximum value?"
[DBG]: PS C:\>>> s
fixme.ps1:5 $pi=[math]::pi
[DBG]: PS C:\>>> s
fixme.ps1:6 $result=$pi*($r*$r)
[DBG]: PS C:\>>> $pi
3.14159265358979
[DBG]: PS C:\>>> $r
5
[DBG]: PS C:\>>> $r.gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Int32 System.ValueType
[DBG]: PS C:\>>> c
78.5398163397448
I am ending
Don't focus on my script (PowerShell actually makes it work despite my imposed "bug"), but rather the technique which also works in the PowerShell ISE. Select the line for the breakpoint and then insert one using the Debug menu or the keyboard shortcut F9.
So, the next time you run into a problem, break your script open and see what's inside.
About the Author
Jeffery Hicks is an IT veteran with over 25 years of experience, much of it spent as an IT infrastructure consultant specializing in Microsoft server technologies with an emphasis in automation and efficiency. He is a multi-year recipient of the Microsoft MVP Award in Windows PowerShell. He works today as an independent author, trainer and consultant. Jeff has written for numerous online sites and print publications, is a contributing editor at Petri.com, and a frequent speaker at technology conferences and user groups.