Variable Scopes and $PRIVATE:

In my previous post Evolution of a Script, Timing Operations, I used the technique of putting “__” in front of variable names to avoid namespace collision.  Niklas Bergius left a comment saying, “Instead of using __ prefixes, wouldn’t $private: work?”   Great question – let’s explore.

The first thing I want everyone to get in focus is that there are lots of ways to accomplish something in PowerShell.  That means that you shouldn’t freak out of you see someone using a technique you don’t use.  The culture of PowerShell is one of exploration and fun.  When you see something new, go explore and find out it’s strengths and weaknesses for yourself.  I’ve always believed that the best way to learn a system is to use it – explore and have some fun.  While PowerShell is a power tool for admins, we put in a number of features that make it easy (and often safe) to explore (e.g. -Whatif).

So let’s explore $PRIVATE:.

What is the first thing we do when exploring something? We type HELP.  In this case, we say HELP PRIVATE (I didn’t provide the ‘$’ otherwise it would try to evaluate it as a variable).  This provides a list of potential help subjects including ABOUT_SCOPES.  $PRIVATE: is a variable SCOPE – you can tell that by the “:”.

So now we want to read the help and explore what it tells us.  The way I like to do this is to use ISE and pipe the help to the clipboard (help About_scopes | clip ) and then paste it into a new editor windows (CTRL-N and then CTRL-V).  Now I can read the help and have the interactive shell to explore.  You should spend some time here – there is a LOT to learn about Scopes.  PowerShell’s implementation of variables is very rich and different from other languages.

Side note: PowerShell separates syntax from implementation.  Variables are just places you store and retrieve data.  Traditionally this is a piece of volatile RAM but in PowerShell it can be anywhere.  For example:  ${C:\Temp\Test.txt} = “This is a test”  stores the string into a file using variable syntax.  This is a trick I learned implementing GT.M MUMPS systems (which were a combined language/database system) in the 1980s.

The help explains the concept of a scope (a mechanism which determines how a element [variable, alias, drive, function, etc.] will be made available [or not] to other elements of the program) and explains the different types of scopes: Global, Local, Script, Private, and Numbered.  The explanation of Private scopes says:

Items in private scope cannot be seen outside of the current scope. You can use private scope to create a private version of an item with the same name in another scope.

So let’s start exploring:
Let’s start with the following as a baseline to remind everyone how PowerShell variables work and the power of dynamic scoping:

  1. #start with a clean slate  
  2. Remove-Variable -Name x    
  3. $x = “1”  
  4. “x = “ + $x  
  5. function test  
  6. {  
  7.     “In test, x = “ + $x  
  8. }  
  9. test  

Running it produces the following output:
x = 1
In test, x = 1

What that means is that when we tried to access the variable X in the function test, it was not available so PowerShell looked into the parent’s scope, found the variable there and used it. Now let’s put $X into a PRIVATE scope and see how things change.

  1. #start with a clean slate  
  2. Remove-Variable -Name x   
  3. $private😡 = “1”  
  4. “x = “ + $x  
  5. function test  
  6. {  
  7.     “In test, x = “ + $x  
  8. }  
  9. test  

(Apologizes if that code shows an emoticon – I’m using a Syntax Highlighter which doesn’t work properly – any recommendations?)

This produces the following result:
x = 1
In test, x =

So what that tells us is that by using $Private, the variable is hidden from other parts of the program.  Niklas’ comment showed a clear understanding of scope and variable handling because if PowerShell doesn’t find the variable in the parent, it continues up the stack looking for the variable until it finds it or fails.  The problem I had with my script was that I wanted to use a variable name in one routine and have child routines be able to use that variable but bind it to it’s grandparents value.  Let’s go through that slowly.

I was trying to write a function Measure-Time which took a variable which I wanted to call $ScriptBlock which I would then execute by calling &$ScriptBlock.  That worked fine EXCEPT when the ScriptBlock refers to a variable named $ScriptBlock (and why wouldn’t it – If I find it useful, others will as well).  The problem is that that then evaluates to itself and bad things ensue.  So what we want to do is to somehow use the variablename ScriptBlock in a way that when ONLY my routine sees it.  When my routine calls a function that walks up the stack looking for a variable called ScriptBlock – I want it to pass right over me and look for it in my parent/grandparent/etc.  That is exactly what $PRIVATE: does.

If none of that makes any sense – don’t worry – lets explore a few things and I think you’ll get it via experimentation.  So here is the experiment (I’m going to switch variable names from X to ScriptBlock so it matches the text above):

  1. #start with a clean slate  
  2. Remove-Variable -Name ScriptBlock -ErrorAction Ignore  
  3. $ScriptBlock = “top”  
  4. “ScriptBlock = “ + $ScriptBlock  
  5. function child  
  6. {  
  7.     “In child,  ScriptBlock = “ + $ScriptBlock  
  8. }  
  9. function middle  
  10. {  
  11.     $ScriptBlock = “middle”  
  12.     “In middle, ScriptBlock = “ + $ScriptBlock  
  13.     child  
  14. }  
  15. middle   

You run that and get:

ScriptBlock = top
In middle, ScriptBlock = middle
In child,    ScriptBlock = middle

When CHILD runs, it tries to access $ScriptBlock and can’t find it so it goes to the parent MIDDLE, finds it and uses it.  Now let’s modify the MIDDLE routine and declare the ScriptBlock variable to be PRIVATE.  What we are trying to do is to hide it from everyone else so that when CHILD looks for it, it skips MIDDLE and finds it at the top scope.  We only change 1 line in MIDDLE:

  1. function middle  
  2. {  
  3.     $Private:ScriptBlock = “middle”  
  4.     “In middle, ScriptBlock = “ + $ScriptBlock  
  5.     child  
  6. }  

You run that and now get:

ScriptBlock = top
In middle, ScriptBlock = middle
In child,    ScriptBlock = top

Success!

So there are 3 things I want you to get out of this blog:

  1. PowerShell has rich semantics so explore SCOPES to understand them.
  2. There are usually multiple ways to solve a problem in PowerShell each with a different set of pros and cons.
  3. Explore and Share.  I shared my work and Niklas reminded me of a better way to solve the problem.  If I didn’t share, I wouldn’t have benefitted from his insight.  Sometimes beginners are embarrassed to share their work because they are afraid of looking like a beginner – DON’T LET THIS BE YOU.   The PowerShell community has a TON of super smart people that are both very helpful and very kind.  Share your work and if there is a better way of doing things, they’ll let you know that without being a jerk about it.  (I hope that is true and someone recommends a new PowerShell syntax highlighter for HTML 🙂
  4. Things don’t have to be perfect – they have to be useful.  (joke intended but it is a serious point)  I could have gone back and done a bunch of work to clean up my examples to get rid of the emoticon but you can cut-n-paste the example and it is going to work fine and if I cleaned it up – I would never get a recommendation for a better syntax highlighter.

Explore, explore, explore!
Cheers!
Jeffrey

13 thoughts on “Variable Scopes and $PRIVATE:

    • In some, possibly many, work places scripts by different authors are “passed around” without regard to naming conventions. A scenario that could lead to the clash of the variables.

      I highly recommend the use of PowerShell’s scoping capabilities, and of course, using modules as opposed to “dot” sourcing.

  1. Are you using SyntaxHighlighter by Alex Gorbatchev? Unfortunately, it hasn’t been updated since 2010 and is probably dead, but I’ve been using it for a couple years, and, while not perfect, it doesn’t do that stuff with the emoticons and the fancy quotation marks.

  2. Nice follow-up.

    After commenting, I realized that function parameter variables will cause trouble. So, using the general PowerShell trial and error methodology, I prefixed them with private:, expecting it to break – it works, as far as I can tell. The only problem with that is that Get-Help Measure-Time and tab completion will show the parameter as -private:ScriptBlock, which can’t be explicitly referenced. AliasAttribute helps with that, but the help text is still misleading and tab completion still breaks (I’d call this a bug – the parameter should show up as the unqualified name).

    I’m beginning to think that your prefix solution is easier for everyone when dealing with function parameters, even though it still might cause a name collision down the line.

  3. Hi Jeffrey!

    Last Month I was on the first German PowerShell Community Conference with Dr. Tobias Weltner.
    I am using the same Syntax Highlighter like he on my WordPress Blog.
    Crayon Syntax Highlighter
    http://wordpress.org/extend/plugins/crayon-syntax-highlighter/?utm_source=twitterfeed&utm_medium=twitter

    We discussed the Highlighter problem because even this very good one fails if you use [XML] Accelerator or such.
    You can Import the colored RTF Text from ISE 3.0 or PowerGUI into Microsoft Office Word and export it with LiveWriter or direct from Word to WordPress. This is acceptable but not best.

    Tobias and I came to the conclusion that only the PowerShell Parser can do this job best.
    Lee Holmes wrote a Syntax Highlighter under use of the PowerShell Parser.
    http://www.leeholmes.com/blog/2008/02/12/realtime-syntax-highlighting-in-your-powershell-console/

    Helge Klein took the work of Lee and made a PowerShell HTML Syntax Highlighter.
    http://blogs.sepago.de/e/helge/2010/01/18/syntax-highlighting-powershell-code-in-html-with-a-powershell-script

    David Wise took the work of Helge and added CSS support to the PowerShell HTML Syntax Highlighter.
    http://blog.davidjwise.com/2013/01/02/powershell-syntax-highlighting-script/

    I think the code of David is best for presenting PowerShell code on the Web.

    Cheers
    Peter Kriegel
    http://www.admin-source.de

  4. I dropped a link to this article on my desktop months ago and I finally got around to reading it. Good stuff. It helped me to understand some issues with scope that I had not taken the time to work through. I use a single underscore for script level variables – old habit, not to avoid collisions but to indicate scope. It helps me to sort them out from the locals. But to each their own!

  5. Pingback: Be careful with that scope | clan8blog

  6. Great aside. I have been playing with storing variables in files and looking for ways to leverage the syntax, and there are some intriguing possibilities. But you hint that more is possible.
    “Traditionally [variable storage] is a piece of volatile RAM but in PowerShell it can be anywhere.”
    Is there more to “anywhere” than just memory and text files?

  7. Absolutely! This area of PowerShell was largely influenced by my work on MUMPS compilers. MUMPS had a variable syntax which could refer to in-memory state or it could do a database lookup.

    SET A(“Snover”, “Jeffrey”, “Title”) = “Distinguished Engineer”
    stored the data in process memory whereas:

    SET ^A(“Snover”, “Jeffrey”, “Title”) = “Distinguished Engineer”
    stored it in a database for all to see.

    We’ve never prioritized the scenario to link in a database but we made it possible for others to do so.

    Jeffrey

  8. Jeffrey, you can try https://highlightjs.org/ for syntax highligthing.

    It’s reasonably actively developing by Ivan Sagalaev, supports powershell and doesn’t have emoticons problem.

    #start with a clean slate
    Remove-Variable -Name x
    $private:x = “1”
    “x = “ + $x
    function test
    {
    “In test, x = “ + $x
    }
    test

Leave a Reply

Your email address will not be published. Required fields are marked *