The Set-StrictMode Cmdlet
The information in this article was written against the second Community Technology Preview (CTP2) of Windows PowerShell 2.0. This information is subject to change in future releases of Windows PowerShell 2.0.
Easier Debugging with the Set-StrictMode Cmdlet
If there’s anything that drives script writers crazy it’s typographical errors; after all, even something as innocuous as a missing or transposed letter can wreak untold havoc on a script. For example, consider a script that multiplies a pair of numbers and then reports back the result:
$intNumber1 = 182 $intNumber2 = 722 $intResut = $intNumber1 * $intNumber2 Write-Host $intResult
So what is 182 times 722? This:
And that is not a typo; the script actually does return a null value. Why? Well, take a careful look at line 3. We were supposed to store the results of our equation in a variable named $intResult; instead, we mistyped that as $intResut. As a, well, result, we get a null value when, in line 4, we echo back the value of $intResult; that’s because $intResult has no value.
Or take this example. Suppose we want to know the last time each file in a specified folder (C:\Scripts) was accessed. To get that information, we run the following command:
Get-ChildItem C:\Scripts | Select-Object Name, LastAccessedTime
Here’s what we get back:
Name LastAccessedTime ---- ---------------- 280.txt add_access_rule.ps1 alice.txt Average.txt bcdscripts.zip bootmgr.vbs bootmgr2.vbs coffee.txt ctp2.txt ctp2_cmdlets.txt
Interesting; apparently no one has ever accessed any of the files in this folder.
If that seems a little hard to believe, there’s a good reason for that: it’s very hard to believe. As it turns out, all of these files have a LastAccessTime property; unfortunately, however, we told the Select-Object cmdlet that the property was named LastAccessedTime. There’s no such property, but PowerShell dutifully tries to retrieve the values anyway. When it can’t, it simply reports back null values for the missing property.
Admittedly, both of these examples are relatively simple, and it probably wouldn’t be too hard to figure out what you did wrong. Unfortunately, that might not always be the case. For example, in a longer script you might run into a problem that’s due to a typographical error made 30 or 40 lines previously; a mistake like that can be very difficult to track down. Likewise, you might be dealing with a property that might actually have a null value. If your script returns nothing at all you don’t know whether that was because there was nothing to return, or if it was because you referenced a non-existent property.
Windows PowerShell 1.0 offers script writers a limited amount of help when it comes to coding mistakes like referencing variables that haven’t been initialized (i.e., that haven’t been assigned a value) or trying to retrieve the value of a non-existent property. You could protect yourself against variables that haven’t been initialized by using this command:
Suppose you do that and you then try to echo back the value of a variable that hasn’t been initialized. When you do that, you’ll get back a message similar to this:
The variable $xyz cannot be retrieved because it has not been set yet. At line:1 char:4 + $xyz <<<<
This works fine, but there is a drawback to using Set-PSDebug: it applies to anything, and everything, you do during your PowerShell session. That’s fine for a new script you’re working on; it helps you catch problems with variables that haven’t been initialized. Unfortunately, though, strict mode also applies to any other script you try running or any other command you type in. Suppose you have a script you obtained from someone else, a script that uses 35 variables, none of them initialized. Is that script going to work? You bet it will … well, just as soon as you go through and initialize each and every variable. Or at least until you turn off strict mode:
Oh, and Set-PSDebug helps with only uninitialized variables; it doesn’t help at all when it comes to referencing properties that don’t exist.
So does the Windows PowerShell 2.0 of Windows PowerShell offer a solution to these problems? Let’s hope so; otherwise this long, drawn-out introduction is going to look a bit silly, isn’t it?
Fortunately for us, the latest version of Windows PowerShell does have a solution to these problems: the new cmdlet Set-StrictMode. Set-StrictMode enables you to raise errors – and terminate a script – if you:
Reference a variable that has not been initialized.
Reference a property that does not exist.
Incorrectly call a function. In most cases this means: 1) putting the function arguments in parentheses (don’t do it); or, 2) separating function arguments using commas (arguments should be separated using blank spaces).
Set-StrictMode includes a parameter named –version that enables you to specify just how strict you want PowerShell to be when running your script. Suppose all you care about are variables that haven’t been initialized. In that case, in your script, set the strict mode version to 1, like so:
Set-StrictMode -version 1 $b
Note. Why did we explicitly state that you should set strict mode in your script? Well, that’s one of the other major advantages to using Set-StrictMode rather than Set-PSDebug: the mode you apply is valid only in the current PowerShell scope. If you set strict mode in the console window then the settings will apply to any command typed at the command prompt; if you set strict mode in a script then those settings will apply only to that particular script. Among other things, that means that you can use strict mode in your scripts (thus forcing you to initialize all variables) without having to worry about the fact that scripts written by someone else might not use this same mode, and thus might not initialize all variables.
So what’s going to happen when we run this simple little script? This is going to happen:
PS C:\scripts> c:\scripts\test.ps1 The variable $b cannot be retrieved because it has not been set yet. At C:\scripts\test.ps1:1 char:3 + $b <<<<
There you go: we referenced a variable ($b) that has never been initialized. As a result, the script grinds to a halt, just the way we wanted it to. But only this script grinds to a halt. If we have other scripts, scripts where we didn’t bother to set the strict mode, those scripts – and their uninitialized variables – will continue to run just fine.
Now, what if you’d also like to trap for nonexistent properties and improperly-called functions? No problem; just set the strict mode version to 2, like we did in this script:
Set-StrictMode -version 2 $a = Get-Date $a.Days
In this script we set the strict mode, then use the Get-Date cmdlet to retrieve the current date and time, storing that value in a variable named $a. We then try to echo back the value of the Days property. But there’s a problem: PowerShell’s date-time object doesn’t have a Days property. (The actual property name is Day.) What’s going to happen when we run this script? See for yourself:
PS C:\scripts> c:\scripts\test.ps1 Property 'Days' cannot be found on this object; make sure it exists. At C:\scripts\test.ps1:3 char:4 + $a. <<<< Days
Tired of strict mode? That’s fine; just use the –off parameter to turn things off:
If only everything in life was that easy, eh?