Quantcast
Channel: Hey, Scripting Guy! Blog
Viewing all articles
Browse latest Browse all 3333

Use PowerShell Strict Mode for Debugging

$
0
0

Summary: Ed Wilson, Microsoft Scripting Guy, talks about using strict mode in Windows PowerShell for debugging purposes.

Microsoft Scripting Guy, Ed Wilson, is here. One of the cool things about Windows PowerShell is that it can take being ignored or being used carelessly and sloppily, and it still seems to work fine. I mean, in the old days with VBScript, it was common to use Option Explicit to force you to declare all of the variables that you were going to use in the script. In Windows PowerShell, no one seems to do this…at all.

The bad thing is that if I run a script (say in the Windows PowerShell ISE) and the variable increments, if I do not set it to a known number (say 0), or even if I initialize it as $null, each time the script runs, the value of the variable changes. This can be bad for consistent results.

So when I have a script that is important and that I want to make sure will always run consistently, I like to initialize my variables—that is, declare them and set them to an initial value.

Here is what I am talking about. The first time I run the following code, I print “less than four” several times:

PS C:\>

For ($i = 1; $i -le 5; $i++)

    {

     $a

     $a += $i

    If ($a -le 4) {"Less than four"}}

0

Less than four

1

Less than four

3

6

10

PS C:\> 

But the second time I run the script, I never reach that If line because the value of $a starts off greater than four:

PS C:\>

For ($i = 1; $i -le 5; $i++)

    {

     $a

     $a += $i

    If ($a -le 4) {"Less than four"}}

15

16

18

21

25

PS C:\> 

The way to correct the problem is to initialize $a as equal to 0 (zero) at the beginning of the script. Now, every time I run the script, I get the correct output:

PS C:\>

 $a = 0

For ($i = 1; $i -le 5; $i++)

    {

     $a

     $a += $i

    If ($a -le 4) {"Less than four"}}

0

Less than four

1

Less than four

3

6

10

PS C:\>

 $a = 0

For ($i = 1; $i -le 5; $i++)

    {

     $a

     $a += $i

    If ($a -le 4) {"Less than four"}}

0

Less than four

1

Less than four

3

6

10

PS C:\> 

It might be useful to be able to force Windows PowerShell to tell me that I am using an uninitialized variable. I can easily do this by using Windows PowerShell and the Set-StrictMode cmdlet.

Using the Set-StrictMode cmdlet

Here is a simple script, I used yesterday in More PowerShell Script Tracing and Strict Mode that will help illustrate using Set-StrictMode:

SimpleTypingErrorNotReported.ps1

$a = 2
$b = 5
$d = $a + $b
"The value of `$c is: $c"

The Set-StrictMode cmdlet can also be used to enable strict mode. It has the advantage of being scope aware. Whereas the Set-PSDebug cmdlet applies globally, if the Set-StrictMode cmdlet is used inside a function, it enables strict mode for only the function.

There are two modes of operation that can be defined when using the Set-StrictMode cmdlet. The first is –version 1, which behaves the same as the Set-PSDebug -Strict command except that scope awareness is enforced. This is shown here:

PS C:\> Set-StrictMode -version 1
PS C:\> C:\fso SimpleTypingError.ps1
The variable '$c' cannot be retrieved because it has not been set.
At C:\fso\SimpleTypingError.ps1:4 char:28
+ 'The value of $c is: ' + $c <<<<
    + CategoryInfo          : InvalidOperation: (c:Token) [], RuntimeException
    + FullyQualifiedErrorId : VariableIsUndefined
PS C:\>

The Set-StrictMode cmdlet is not able to detect the uninitialized variable contained in the expanding string that is shown in the SimpleTypingErrorNotDetected.ps1 script.

When –version 2 is enacted, the technique of calling a function like a method is stopped. The AddTwoError.ps1 script passes two values to the add-two function via method notation. Because method notation is allowed when calling functions, no error is normally generated.

But method notation of passing parameters for functions only works when there is a single value to pass to the function. To pass multiple parameters, function notation must be used, as shown here:

add-two 1 2

Another way to call the add-two function correctly is to use the parameter names when passing the values, for example:

add-two -a 1 -b 2

Either of the two syntaxes would produce the correct result. The method notation of calling the function displays incorrect information but does not generate an error. An incorrect value being returned from a function with no error being generated can take a significant amount of time to debug. The method notation of calling the add-two function is used in the AddTwoError.ps1 script, and it is shown here:

add-two(1,2)

When the script is run and the Set-StrictMode -version 2 command has not been enabled, no error is generated. The output seems to be confusing because the result of adding the two variables $a and $b is not displayed:

PS C:\> C:\fso\AddTwoError.ps1
1
2
PS C:\>

When the Set-StrictMode -version 2 command is entered and the AddTwoError.ps1 script is run, an error is generated. The error that is generated states that the function was called as if it were a method. The error points to the exact line where the error occurred and shows the function call that caused the error. The function call is preceded with a plus sign (+) followed by the name of the function, followed by four arrows that indicate what was passed to the function. The error message is shown here:

PS C:\> Set-StrictMode -version 2
PS C:\> C:\fso\AddTwoError.ps1
The function or command was called as if it were a method. Parameters should be
 separated by spaces. For information about parameters, see the about_Parameters Help topic.
At C:\FSO\AddTwoError.ps1:7 char:8
+ add-two <<<< (1,2)
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : StrictModeFunctionCallWithParens
PS C:\>

The complete AddTwoError.ps1 script is shown here:

AddTwoError.ps1

Function add-two ($a,$b)
{
 $a + $b
}

add-two(1,2)

When you specify Set-StrictMode for Windows PowerShell version 2.0, it checks the following items:

  • References to uninitialized variables, directly and from within expanded strings
  • References to nonexistent properties of an object
  • Functions that are called like methods
  • Variables without a name

If you specify Set-StrictMode for Windows PowerShell version 1.0, it only checks for references to uninitialized variables.

If you are not sure whether you want to use strict mode for Windows PowerShell versions 2.0 , 3.0, 4.0, or 5.0 (there are no changes since strict mode 2), an easy way to solve the problem is to use the value Latest. By using Latest for the value of the -version parameter, you always ensure that your script will use the latest strict mode rules. This technique is shown here:

Set-StrictMode -version latest

One issue that can arise with using Latest is that you do not know what the latest changes might do to your script, and a new set of rules might break your script. Therefore, it is generally safer to use –version 1 or –version 2 when looking for specific types of protection.

To turn off strict mode, use the –Off parameter:

Set-StrictMode -Off

That is all there is to using Windows PowerShell strict mode. Debugging Week will continue tomorrow when I will talk about more cool stuff.

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 


Viewing all articles
Browse latest Browse all 3333

Trending Articles