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:
- #start with a clean slate
- Remove-Variable -Name x
- $x = “1″
- “x = ” + $x
- function test
- {
- “In test, x = ” + $x
- }
- 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.
- #start with a clean slate
- Remove-Variable -Name x
- $private
= “1″ - “x = ” + $x
- function test
- {
- “In test, x = ” + $x
- }
- 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):
- #start with a clean slate
- Remove-Variable -Name ScriptBlock -ErrorAction Ignore
- $ScriptBlock = “top”
- “ScriptBlock = ” + $ScriptBlock
- function child
- {
- “In child, ScriptBlock = ” + $ScriptBlock
- }
- function middle
- {
- $ScriptBlock = “middle”
- “In middle, ScriptBlock = ” + $ScriptBlock
- child
- }
- 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:
- function middle
- {
- $Private:ScriptBlock = “middle”
- “In middle, ScriptBlock = ” + $ScriptBlock
- child
- }
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:
- PowerShell has rich semantics so explore SCOPES to understand them.
- There are usually multiple ways to solve a problem in PowerShell each with a different set of pros and cons.
- 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
- 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