PowerShell Variable Scope

I did some more reading on Powershell variable scope definition today and thought I’d write up what I found. I’m undecided on the best practice for writing functions especially given the patchy documentation on passing variables to a function. One way is to create some variables in the main script and just use them in the function although there are some caveats to doing that which I’ll cover in a second. the other way is to explicitly pass the variables to the script, even ones you are not going to change within the function itself. The benefit of the latter option is when reusing the function in another script you can see all the variables that need to be available in the parameter definition. This approach can lead to long parameter lists and for simplicity I think most people tend to use the former strategy and this is where a discussion on variable scope is useful because it can have a big impact on the functionality of the script.

Variables have the following scope: GLOBAL, SCRIPT , LOCAL and PRIVATE

  • Global – available everywhere in the run space – so even without dot sourcing the script the variable will be available at the command line. ( dot sourcing is this . .\mypowershellscript.ps1 the period followed by a space makes the script stay loaded in memory after it completes and all script variables are available at the command line or to any subsequent scripts you run in the same run space).
  • Script – available everywhere inside the script and inside any scripts called within the parent script. As far as the script is concerned this is the same as Global and in fact as you can see, just to confuse things you can reference a script scope variable using the same command as you would the script scope variable. When the script finished though the variable is destroyed and you cannot reference it from the command line. NOTE: this does not apply within an ISE which will often make every variable available as if it’s a global variable. This means you need to test your script at least once from the command line if it calls other scripts. What works in the ISE may not when you run it as a standalone script.
  • Local – available within the script or function only. A variable created at the start of a script is by default of local scope. Think about this, though. the variable is local but will act exactly the same as a script scope variable and as a global variable and can again be referenced in the same way as I suggested when discussing the Script scope definition.
  • Private – is only available within the scope of the variable and does not bleed into any other scope, even if you create the variable at the beginning of the main script the variable cannot be seen inside any of the called functions or scripts.

Pretty complex then!

here’s a little script that demonstrates this and it’s a modified one taken from this blog posting: http://www.jsnover.com/blog/2013/03/17/variable-scopes-and-private/

But before I get into the script just some quick notes for you……got your pens ready?

  1. By default all variables are scope local – but this can effectively mean its script scope or even global scope if it’s at the top of your script and not inside a function.
  2. All child functions have read access to the variables created by the parent or grandparent or great gran parent or …. you get the idea.
  3. You can create a variable by using the new-variable cmdlet and specify the scope using the -scope parameter
  4. It’s quicker to create a variable by just declaring it e.g. x=1
  5. You can prefix the variable name with the scope e.g. $global:x=1 or $script:x=1 or $local:x=1 or $private:x=1
  6. When you prefix a variable with it’s scope it is read and write, i.e. you can update the value.
  7. If you don’t prefix the variable then PowerShell will search for the variable in the private scope then local, then script and then finally global scope before giving up and sulking. Don’t forget point 2 if it’s not local scope then it’s a read only variable but read ahead to point 8 before you draw any conclusions πŸ™‚ ( this is a bit like Bill and Teds excellent Adventure – just not as interesting)
  8. When assigning a value to a variable PowerShell will search the local scope to see if it can find the variable already and if id does then it assigns the value if it can’t find one then it assumes that you want to create a new local scope variable – if you want to update the value in a child scope, e.g. a function use the scope prefix, i.e. script:x=2
  9. Point 8 can mean a world of pain when you overwrite variables in child scopes ( functions called from the main script)
  10. It’s far too easy to be sloppy when programming and naming variables – and even easier to get spaghetti sauce on a white shirt.

#start with a clean slate

Remove-Variable -Name ScriptBlock -ErrorAction Ignore

$ScriptBlock = “top”

$Private:onlyinmain = “only in the main”

“======================================================”

“In main,ScriptBlock = “ + $ScriptBlock

“In main, middlevariable = “ + $middlevariable

“In main, onlyinmain = “ + $onlyinmain

“======================================================”
function child

{

“======================================================”

“In child, ScriptBlock = “ + $ScriptBlock

“In child, Script:ScriptBlock = “ + $Script:ScriptBlock

“In child, onlyinmain = “ + $onlyinmain

“In child, middlevariable = “ + $middlevariable

“======================================================”

}

function middle

{

$Private:ScriptBlock = “middle”

$middlevariable = “Middle”

“======================================================”

“In middle, ScriptBlock = “ + $ScriptBlock

“In middle, Script:ScriptBlock = “ + $Script:ScriptBlock

“In middle, onlyinmain = “ + $onlyinmain

“In middle, middlevariable = “ + $middlevariable

“======================================================”

child

}

middle

“======================================================”

“In main,ScriptBlock = “ + $ScriptBlock

“In main, middlevariable = “ + $middlevariable

“In main, onlyinmain = “ + $onlyinmain

“======================================================”

. middle

“======================================================”

“In main, middlevariable = “ + $middlevariable

“======================================================”

There are two functions middle and child. Child is nested in the middle function so is only available to the middle function. You can’t call it from the main script. At the start of the script I create a private variable just to prove you can’t see it anywhere but in the main script body. Then I call the middle function and this prints to screen the values of the variables – then middle calls child and does the same. Notice the variable scriptblock in middle is of private scope to middle and is not available in child – so Powershell searches back through the scopes and finds the scriptblock variable we set in the main script. the variable we create in middle, middlevariable is available in middle and child but not in the main script.

======================================================
In main, ScriptBlock = top
In main, middlevariable =
In main, onlyinmain = only in the main
======================================================
======================================================
In middle, ScriptBlock = middle
In middle, Script:ScriptBlock = top
In middle, onlyinmain =
In middle, middlevariable = Middle
======================================================
======================================================
In child, ScriptBlock = top
In child, Script:ScriptBlock = top
In child, onlyinmain =
In child, middlevariable = Middle
======================================================
======================================================
In main, ScriptBlock = top
In main, middlevariable =
In main, onlyinmain = only in the main
======================================================
======================================================
In middle, ScriptBlock = middle
In middle, Script:ScriptBlock = middle
In middle, onlyinmain = only in the main
In middle, middlevariable = Middle
======================================================
======================================================
In child, ScriptBlock = middle
In child, Script:ScriptBlock = middle
In child, onlyinmain =
In child, middlevariable = Middle
======================================================
======================================================
In main, middlevariable = Middle
======================================================

So what’s the best strategy for calling functions and using variable scope…… Hmmmm I’ll need to think about this some more before I can post my opinions on this one now that I have a better understanding of scope I may change my current script writing style to utilise this knowledge.

If your still with me and interest why I suddenly started researching this – it’s because I’ve been handed over the development of an application built on a bunch of powershell scripts written by someone else. Their style was to use . sourcing every where and call global variable scope which works but it’s very hard to follow and debug. We need to maintain some variable naming standards and always think about the scope of variables. I’ve grown to hate . sourcing unless it’s for a quick and dirty script when is when that technique is brilliant. In a production level script I’d advise you stay away from it. The other thing I encountered in these scripts was using dos command and paths based on using dots to navigate to different directories – wow, like I don’t have enough things to worry about now we just added folder location of the running script and all the .sourced scripts running in different locations.

One last gripe with . sourcing before I go, when you get an error it give you a line number that nears no relation to the line number in any of the scripts and the actual line of code could be in any one of the . sourced scripts called in the main script. ARRRRGHHHHHH – nothing a few beers wont fix though – well you wont care about fixing it, for a while at least and to be fair us scripters could probably do with being more sociable so maybe that’s not such a bad thing after all πŸ™‚ .

Here are some more links discussing variable scope – if I haven’t already bored you to death with this post.

http://blogs.technet.com/b/heyscriptingguy/archive/2010/02/11/hey-scripting-guy-february-11-2010.aspx

http://www.jsnover.com/blog/2013/03/17/variable-scopes-and-private/

http://www.sapien.com/blog/2013/03/06/first-rule-of-powershell-scoping-rules/

Sadly I’ve just broken rule No. one from the link above…..

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.