Showing posts with label Variables. Show all posts
Showing posts with label Variables. Show all posts

Sunday, May 31, 2009

Using Strict Mode with Set-PSDebug

If you've used Perl in the past, then you're probably familiar with "use strict" (if you've been using Perl without "use strict", then head over here Note: the scoping issues don't apply to PowerShell).

By default, PowerShell is very accommodating. If you tell it to echo the value of $chouderhead, it will happily echo... NOTHING! There is no variable called $chouderhead, you misspelled $chowderhead. PowerShell doesn't like to make waves, though, so it happily creates a new variable named $chouderhead and starts using it. This can lead to very hard-to-debug issues.

You can fix this By adding this line to your profile (or just typing it in at the prompt):

PS> Set-PSDebug -strict

Now if you try to use a variable that hasn't yet had a value assigned to it, you'll get an error like this:

PS> $chouderhead
The variable '$chouderhead' cannot be retrieved because it has not been set.
At line:1 char:13
+ $chouderhead <<<<
+ CategoryInfo : InvalidOperation: (chouderhead:Token) [], RuntimeException
+ FullyQualifiedErrorId : VariableIsUndefined

The error is annoying, but if you've ever wasted a half an hour wondering why your script isn't outputting what you thought it would, only to realize that on line 304 you accidentally used an 'm' instead of an 'n' in your variable name and how could I miss that, it's so stupid, and now I missed dinner, and my back hurts from crouching over my keyboard staring at the screen for too long and curse the day I was born! Why, oh, why didn't I just add 'PSDebug -strict' to my profile? Why?

But I digress...

Sunday, July 6, 2008

Fun and Excitement with Variable Scoping!!!

Since PowerShell's syntax in scripts reminds me of Perl, my fingers keep trying to type the word "my" before I set every variable (you Perl geeks know what I'm talking about). Anyway, that got me thinking about scoping, which is amazing and exciting, and the most fun topic in the world.

Okay, so I was being sarcastic, but it is pretty important, and it turns out that there is a very good article on scoping right here. I'll try to break it down to a shorter, simpler version for the purposes of the blog, so here goes:

Let's say I do this:

PS C:\> $x = "Global"
PS C:\> function print_var {echo $x}
PS C:\> print_var

You'd expect it to output the string "Global", right?

What if I do this?

PS C:\> echo $x
PS C:\> function set_var {$x = "Local"; echo $x}
PS C:\> set_var
PS C:\> echo $x

You might expect the output to be

Global
Local
Local

but it's not. The output is

Global
Local
Global

That's because by default each variable is set in the local scope. In the case of the function set_var, a new variable named $x is created inside the script block that masks the global $x variable that was set outside of it. When PowerShell looks up the value of a variable, it starts in the local scope and then works its way up, to the parent scope, and then its parent, and so on until it reaches the global scope. If you accidentally create a new variable with the same name at a lower scope, PowerShell won't care that you also have a global variable with that name because it will stop looking as soon as it finds one.

The article I referenced earlier has some neat tricks you can do to set scope, and you can look up the help for Set-Variable for more info.

As a general rule it is best to just not set the value of a global variable inside a script block. It is better to return the value of the local variable as the output of the function and use that to set the variable in the parent scope. If you must set a global variable inside a script block, though, there are two ways to do it:

#1

PS C:\> echo $x
PS C:\> function set_var {$global:x = "Local"; echo $x}
PS C:\> set_var
PS C:\> echo $x

#2

PS C:\> echo $x
PS C:\> function set_var {Set-Variable -scope global "Local"; echo $x}
PS C:\> set_var
PS C:\> echo $x

There are two easy rules that apply to any language and will help mitigate this issue without having to think about it too much, though:

Rule #1: Use as few global variables as absolutely necessary.
Rule #2: Never use generic names for variables like $x.