on 17-Dec-2008 11:42
Welcome to this addition of the PowerShell ABC's where you'll find 26 posts detailing a component of the PowerShell scripting language, one letter at a time. Today's letter is the letter "D". For "D" I've picked the a term that most everyone who writes scripts needs to understand. The word for today is debugging.
Debugging is a very large category so I'll scan over the various components and dig more into script level debugging since that is what most users will use most often.
PowerShell implements two types of errors: terminating and non-terminating. Terminating errors will stop operation immediately on the first error while non-terminating errors will simply be errors that are put into the output pipeline and processed by the calling code. This is where the ErrorRecord object and the error string come into play. This can be controlled by setting the error action preference in the ErrorActionPreference variable with the values of "continue", "silentlycontinue" or "stop".
When terminating errors occur, you'll likely want to know how to trap them to determine the extent of the exception. The trap statement is just what you need. The trap statement can occur anywhere in a block of code and when an exception occurs that is not otherwise handled, control will be transferred to the body of the trap statement. The syntax of the trap statement is as follows:
trap [] {
}
By using the break or continue commands, you can control whether script execution is halted or allowed to continue after your block of code is executed. Here are some examples of the trap statement:
PS > trap { Write-Host "Caught it!"; break; } 1/$zero; Write-Host "Made it";
Caught it!
PS > trap { Write-Host "Caught it!"; continue; } 1/$zero; Write-Host "Made it";
Caught it!
Made it!
Anyone who has written a script is familiar with "printf" style debugging where use the output stream to print custom messages included in the script to help with post execution evaluation. PowerShell includes a set of host APIs for just that reason. The $host variable contains all of these APIs and there are a couple of Cmdlet's written to help with the most common of these APIs. The Clear-Host, Get-Host, Out-Host, Read-Host and Write-Host cmdlets should get you most of the way there. The above trap example makes use of the Write-Host Cmdlet for displaying output to the console.
Let's say you have a working script and don't want to muddy things up with a bunch of printf type statements, you can make use of the Set-PSDebug cmdlet that exposes some built-in debugging capabilities of PowerShell.
Set-PSDebug [-Trace ] [-Step] [-Strict]
Set-PSDebug -off
The -Trace option enables basic script tracing. In trace mode, each statement executed by the interpreter will be displayed on the console. A value of 0 will turn script tracing off, a value of 1 will trace script lines as they are executed, and a value of 2 will trace script lines, variable assignments, function calls and scripts.
PS> Set-PSDebug -Trace 1
PS> Write-Host "Hi There!"
DEBUG: 1+ Write-Host "Hi There!"
Hi There!
PS> Set-PSDebug -Trace 2
PS> $a = 1 + 1
DEBUG: 1+ $a = 1 + 1
DEBUG: ! SET $a = '2'.
The -Step option allows you to have control on a step-by-step option for the statements in your script allowing you to control whether the statement is executed or not. Here's an example of the above addition with step enabled.
PS> Set-PSDebug -Trace 1 -Step
PS> $a = 1 + 1
Continue with this operation?
1+ $a = 1 + 1
[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"): y
DEBUG: 1+ $a = 1 + 1
DEBUG: ! SET $a = 2
On final tool you can use is the -Strict option. Similar to Visual Basic's "Option Explicit" or "strict mode" in Perl, the -Strict option will force access to undefined variables to throw exceptions. Normally in PowerShell if a variable is undefined it is treated as if it had the value of $null. $null is a numeric expression treated as zero. With the -Strict option on, undefined variables are not given this assignment. There are cases where running your script in non-strict mode is preferred but this may be a tool that you can use to find any unintentional typos in your code like this:
PS> $myvar = 0
PS> 1 + $mybar
1
PS> Set-PSDebug -Strict
PS> 1 + $mybar
The variable $mybar cannot be retrieved because it has not been set yet.
At line:1 char 11
+ 1 + $mybar
The last type of tracing is the internal exception tracing facility that is designed for use by application developers, not end-users. This traces the execution of the engine at the level of object constructor and method calls and thus will likely cause a lot of output that not required in most cases.
The Trace-Command Cmdlet is used to control low-level expression tracing.
Trace-Command [-Name] [-Expression]
[[-Option] ]
[-ListenerOption ]
[-FilePath ]
[-Debugger]
[-PSHost]
The -Name option specifies the category of activity to trace. The -Expression specifies the expression PowerShell will trace. The PSTraceSourceOptions specify the code elements to trace. The TraceOptions controls the details of what is logged. And the FilePath, Debugger, and PSHost controls where the trace output goes. Since the output is very verbose, I'm not going to provide any samples here. If you want to learn more about the Trace-Command Cmdlet, type "Get-Help Trace-Command -Full" in PowerShell for usage and examples.
The final item in this post will cover the PowerShell Event Log. PowerShell uses the Windows Event Log for it's logging facility. The Get-EventLog Cmdlet can be used to extract the entries in the PowerShell logs.
PS> (Get-EventLog "Windows PowerShell")[-5..-1]
Index Time Type Source EventID Message
----- ---- ---- ------ ------- -------
5 Mar 07 21:48 Info PowerShell 600 Provider "Registry" is Started. ...
4 Mar 07 21:48 Info PowerShell 600 Provider "Function" is Started. ...
3 Mar 07 21:48 Info PowerShell 600 Provider "FileSystem" is Started. ...
2 Mar 07 21:48 Info PowerShell 600 Provider "Environment" is Started. ...
1 Mar 07 21:48 Info PowerShell 600 Provider "Alias" is Started. ...
So, now you have all the tools you need to get debugging in PowerShell. For more information, check out the various help commands in PowerShell itself, or another great reference is the book Windows PowerShell In Action Bruce Payette.
-Joe