Avoid PowerShell Errors by Initializing Variables

Avoid PowerShell Errors by Initializing Variables

  • Comments 2
  • Likes

Summary: Microsoft Scripting Guy, Ed Wilson, talks about using the Set-StrictMode Windows PowerShell cmdlet to aid in detecting uninitialized variables.

Microsoft Scripting Guy, Ed Wilson, is here. Charlotte, North Carolina is alive with color this morning. There is a blue jay sitting on the light post at the end of the driveway, and a cardinal munching on our newly sewn grass seed in the front yard. The various bushes, trees, and shrubs are blooming in the neighborhood. But the chief color right now is the yellowish-green color of the pollen from all the tall willowy southern pines. These evergreens guarantee color all year long; although right now, their chief contribution is the color of pollen. It will be this way for the next few weeks. The rain yesterday knocked it from the air, and left it on the grass, the drive, and the sidewalks of our neighborhood. The Scripting Wife promptly closed the windows and turned on the air conditioner. Me? The pollen does not bother me too badly, so I prefer to sit on the lanai with my laptop, the ceiling fan, a cup of English Breakfast tea, and a freshly baked blueberry scone.

That’s the nice thing about wireless—I can now work the way I prefer to work instead of being tied to a within 10 feet of a LAN drop. Beyond peaceful coexistence with my feathered friends, there is another reason I am sitting on the lanai…I am looking for the cable truck because my Internet connection suddenly dropped off this morning while I was checking my email. This is a real shame because I am missing a meeting I really wanted to attend. Oh well. At least I have my Windows7 phone. (Disclaimer: my employer, the Microsoft Corporation, bought me a Windows7 phone. They did not buy the Windows7 phone that the Scripting Wife uses, but she had to have one after she saw how cool my phone was.) So I can keep in touch with all the members of my team—in fact, I used it to look up the support phone number of my ISP.

The problem of uninitialized variables

One of the cool things about writing Windows PowerShell scripts (indeed, writing most scripting languages) is that you can quickly whip up a bit of code to solve problems. Some languages make this easier than others, some are quite verbose, and others are rather terse, but one advantage of scripting is that it permits rapid development.

This advantage is also a disadvantage. It permits bad habits. As long as you are aware of the bad habits and their attendant consequences, you can use this to your advantage. After all, the goal is to quickly solve a problem. But every so often, these bad habits can cause horrible consequences. This situation usually occurs when an impending deadline is rapidly barreling down the track, and the svelte one-liner has morphed into a 550-line FrankenScript. Having stood at the end of a darkened tunnel with a single light looming large upon my countenance whilst frantically attempting to unravel a mess of spaghetti code, I decided to embark upon a quest for Windows PowerShell best practices (indeed, that was one reason for my book). 

In the script that follows, the value of the $a variable increments five times each time the script runs. This is because the value of $a is never initialized.

UnitializedVariable.ps1

1..5 | foreach{

 $a++}

$a

The result of running this script three times is shown in the image that follows.

Image of script

The problem with such a script, especially when it is run from within the Windows PowerShell ISE, is that the initial value of $a changes each time the script runs. Therefore, you never know the final value of the $a variable. If you are counting something like running processes, stopped services, or errors in a particular event log, it is difficult to know what the expected value might be; and therefore, it is easy to let the error slip by undetected.

Detecting and solving the problem of uninitialized variables

The easiest way to detect the problem of uninitialized variables is to use the Set-Strictmode cmdlet. To do this, add the command at the top of the script. One of the great things about the Set-Strictmode cmdlet is that it only affects the current scope and all child scopes (as opposed to using the older Set-PSDebug –strict command, which affects the global scope).

When it is added, the Set-Strictmode cmdlet detects uninitialized variables and generates an error. The Set-Strictmode cmdlet does this whether the value of the Version parameter is 1, 2, or Latest. The following code shows adding the Set-StrictMode cmdlet to the top of the previous script.

Set-StrictMode -Version 1

1..5 | foreach{

 $a++}

$a

 Image of script

Use the version to apply the right amount of protection

It is important to keep in mind what -Version 1 and -Version 2 check for in strict mode. -Version 1 checks for uninitialized variables. See yesterday’s blog, Use Strict Mode for PowerShell to Help You Write Good Code, for a discussion of the -Version 2 checks.

Correcting the problem of the uninitialized variable requires declaring the variable and assigning a value to it. This technique is shown here.

InitializedVariable.ps1

Set-StrictMode -Version 1

$a = 0

1..5 | foreach{

 $a++}

$a

Now when the script runs, the correct value returns. This is shown in the following image.

Image of script

One thing to keep in mind about uninitialized variables and using Set-StrictMode and –Version 1, is that while it will detect an uninitialized variable that is directly accessed, it permits access to an uninitialized variable within a string. For example, when I use the Set-StrictMode cmdlet to set strict mode -Version 1, an error occurs when directly accessing an uninitialized variable. This technique is shown here.

Set-StrictMode -Version 1

$a

However, if I attempt to access the value of the variable from within an expanding string, no error generates. This technique is shown here.

"the value of `$a is $a"

The previous commands and their associated output are shown in the image that follows.

Image of command output

If accessing an uninitialized variable from within a string is a problem, the answer is to use Set-StrictMode –Version 2. When I set strict mode -Version 2, uninitialized variables within strings also generate an error. The command is shown here.

Set-StrictMode -Version 2

When set, both of the two commands that follow generate errors.

$a

"the value of `$a is $a"

The previous commands and their associated output are shown in the image that follows.

Image of command output

Join me tomorrow for more cool Windows PowerShell 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 

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Hi Ed,

    Avoiding errors is ALWAYS a very good idea!

    So I really appreciate each progress in this direction that is integrated into the next Powershell version ... "If the Powershell team can do the work, why should I complain ..." !? :-)

    The more automatisms to produce secure code are available ... the less error prone is our code! Don't turn any error checking off ... even in case you can't run a script, because you are not able to detect the reason for any error messages produced by the runtime ... don't believe that everything will be better, if you can only get rid of the warning or error messages!

    Always imagine that you have to write thousands of lines of code, maybe spread across files or being parts of modules ... You will definitely be deeply grateful if the system supports you by detecting some errors, you don't have to hunt yourself!

    Klaus (Schulte)

  • @K_Schulte you are right. It is always good to avoid errors. In addition, one of the best ways to avoid errors and to therefore simplify troubleshooting is to write easy to read and easy to understand code. Using the Set-StrictMode cmdlet aids in this direction.