Summary: Learn how to inspect variable values by using the Windows PowerShell debugger.
Microsoft Scripting Guy Ed Wilson here. In yesterday’s post, I talked about using the Set-PSBreakpoint cmdlet to set a breakpoint on a specific script. Today, I want to continue looking at the Set-PSBreakpoint cmdlet.
One of the things I mentioned was that when setting a breakpoint on a script, the script specified must be the same script that breaks. For example, if I set a breakpoint for the c:\fso\mydebug.ps1 script, the only script that breaks when a breakpoint reaches is the c:\fso\mydebug.ps1 script. This is the exact script, from the exact location.
If, on the other hand, I do not specify the script when I create the breakpoint, any script I run causes the breakpoint to trigger. To test this out, I create a script with several commands in it. The script is shown here:
Get-Process.ps1
Get-EventLog application -Newest 1
get-process powershell
Get-Date
I also use the test script from yesterday. The mydebug.ps1 script is shown here:
MyDebug.ps1
$cn = "localhost"
$process = "notepad"
Get-WmiObject -Class win32_bios -cn $cn
Start-Process $process
Get-Process $process
I use the Set-PSBreakpoint cmdlet to create a breakpoint that will break when the command Get-Process appears in a script. The command and associated output are shown here:
PS C:\> Set-PSBreakpoint -Command get-process
ID Script Line Command Variable
-- ------ ---- ------- --------
1 get-process
PS C:\>
When the Get-Process command from the MyDebug.ps1 script appears, the debugger breaks into the script. This allows for the use of debugging commands (the commands appear in a table from yesterday’s article). The C command tells the debugger to continue. In the Get-Process.ps1 script, the script also contains a call to Get-Process, so that script causes the breakpoint to break as well. These two scripts and the associated output including debugger are shown in the following figure.
One of the really cool things to do with a breakpoint is to specify an action to take when a condition occurs. The action to occur is placed inside a scriptblock. In the following command, I specify that when the Get-Process command appears, the debugger will start the notepad process:
Set-PSBreakpoint -Command get-process -Action {notepad}
I modify the Mydebug.ps1 script so that I comment out the Start-Process line. The revised script is shown in the following figure.
When run, the script generates an error because it attempts to use Get-Process to retrieve a nonexistent process. In the breakpoint previously specified, however, when the breakpoint is reached, the scriptblock creates the notepad process, so the script works without causing an exception. This is shown in the following figure.
When debugging a script, often I am concerned about the value of a variable. There are three ways to break on a variable. The three ways are read, write, and readwrite. The default value is write. When working with breakpoints on variables, read or write do not talk about the way the variable is declared in the script, but each determines when the script breaks. For example, when breaking on a variable in write mode, it means the script breaks before a new value is written to a variable. In the case of a variable that is not yet declared, the script breaks just before a value is written or assigned to the variable. I used the code that follows to set a breakpoint on the variable cn when a value is written to the variable:
Set-PSBreakpoint -Variable cn -Mode write
I run the script, and the script breaks before the value “localhost” is assigned to the $cn variable. I then check the value of the $cn variable; nothing is displayed because the variable has not been created. Next, I use the L command to see the code, and I see the script is on the first line, waiting to assign a value to the $cn variable. I then use the C command, and continue execution of the script. The error is generated because I have commented out the Start-Process command (this can be seen in the code listing from the previous command).
In the following figure, I run the C:\fso\debug.ps1 script. The script hits the $cn variable breakpoint and breaks into the script. I check the value of the $cn variable and see it has not been set; thereforem nothing returns. I then assign the value mred to the $cn variable, and I use the L command to view the code. Next, I use the S command to step to the next line in the script. I see that it sets the value notepad for the $process variable. I then step (S) to the next line in the script where I see that it will make a WMI call to retrieve BIOS information via the Win32_BIOS WMI class. I then step past that (S) and see that the script is getting ready to retrieve process information about the notepad process. However, Notepad is not running because the Start-Process command is commented out. I therefore type Start-Process $Process and then step into the remainder of the script.
When I break on a variable in read mode, the value of the variable has already been read. This means I can inspect the value of the variable to ensure the script works as intended. In the code that follows, I remove all the current breakpoints, and then I create a new breakpoint that watches the variable $cn to see when it reads the value of the variable. When the value of the $cn variable is read, the script breaks and enters the debugger:
Get-PSBreakpoint | Remove-PSBreakpoint
Set-PSBreakpoint -Variable cn -Mode read
When I query the value of the $cn variable inside the debugger, I see that the variable contains the value “localhost.” That is what I want, so I step over the remainder of the code, thereby executing the remainder of the script. These commands and associated output appear in the following figure.
That’s it for now. Join me tomorrow for more fun with Windows PowerShell.
I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy