Summary: Microsoft PowerShell MVPs, Don Jones and Jeffery Hicks, talk about the impact of Windows PowerShell scope when creating tools.
Microsoft Scripting Guy, Ed Wilson, is here. This week we will not have our usual PowerTip. Instead we have excerpts from seven books from Manning Press. In addition, each blog will have a special code for 50% off the book being excerpted that day. Remember that the code is valid only for the day the excerpt is posted. The coupon code is also valid for a second book from the Manning collection.
This excerpt is from Learn PowerShell Toolmaking in a Month of Lunches
By Don Jones and Jeffery Hicks
More than likely, your toolmaking projects will be on the complex side, and if you don’t understand scope, you may end up with a bad tool. Scope is a system of containerization. In some senses, it’s designed to help keep things in Windows PowerShell from conflicting with one another. In this blog, which is based on Chapter 5 of Learn PowerShell Toolmaking in a Month of Lunches, you get to see scope in action
If you ran a script that defined a variable named $x, you’d be pretty upset if some other script also used $x and somehow messed up your script. Scope is a way of building walls between and around different scripts and functions, so that each one has its own little sandbox to play in without fear of messing up something else.
There are several elements within Windows PowerShell that are affected by scope:
- Variables
- Functions
- Aliases
- PSDrives
- PSSnapins (but oddly not modules, so as things migrate mainly to modules and away from PSSnapins, this won’t matter much)
The shell itself is the top-level, or global, scope. That means that every new Windows PowerShell window you open is an entirely new, standalone, global scope—with no connection to any other global scope. The ISE lets you have multiple global scopes within the same window, which can be a bit confusing. In the ISE, when you click the New PowerShell tab on the File menu, you’re creating a new Windows PowerShell runspace—which is equivalent to opening a new console window. Each of those tabs within the ISE is its own global scope. The following image shows what that looks like in the ISE. Note that it’s the top, rectangular tabs that represent separate global scopes. The rounded tabs that hold script files live within that runspace, or global scope.
Each script that you run creates its own script scope. If a script calls a script, the second script gets its own script scope. Functions have their own scope, and a function that contains a function gets its own scope. As you can imagine, this can result in a pretty deep hierarchy, which the following image illustrates with a few global scope examples. There’s even terminology for the scopes’ relationships: a scope’s containing scope is called its parent; any scopes contained within a scope are its children. So the global scope is only a parent (because it’s the top-level scope), and it contains children.
So here’s the deal: If you create a variable within a script, that variable belongs to that script’s scope. Everything inside that same scope can “see” that variable and its contents. The scope’s parent can’t see the variable.
Any child scopes, however, have an interesting behavior. Imagine a script named C:\Tools.ps1, in which we create a variable named $computer. Within that script, we have a function named Get-OSInfo. (Sound familiar?) If Get-OSInfo attempts to access the contents of $computer, the operation will work.
But if Get-OSInfo attempts to change the contents of $computer, it will create a new variable, also named $computer, within its own scope. From then on, the function will be accessing its private version of $computer, which will be independent of the $computer in the script scope. This, as you imagine, can be crazy confusing, so let’s see it in action to clarify. The following listing is a script that will help demonstrate scope.
Listing 1: Script.ps1 demonstrating scope
$var = 'hello!'
function My-Function {
Write-Host "In the function; var contains '$var'"
$var = 'goodbye!'
Write-Host "In the function; var is now '$var'"
}
Write-Host "In the script; var is '$var'"
Write-Host "Running the function"
My-Function
Write-Host "Function is done"
Write-Host "In the script; var is now '$var'"
Let’s run that and check out the results:
PS C:\> .\script.ps1
In the script; var is 'hello!'
Running the function
In the function; var contains 'hello!'
In the function; var is now 'goodbye!'
Function is done
In the script; var is now 'hello!'
Try it now…
Please, definitely run this script on your own—we want you to see the results for real, right in front of your eyes. It’ll make it all clearer.
Read through the script’s output. Notice that at the start of the script, $var contains hello! because that’s what the first line in the script set it to. Then the function runs, and it sees that $var contains hello! That’s because $var doesn’t exist in the function’s scope, so when it tries to access $var, Windows PowerShell goes to the scope’s parent. Lo and behold, there’s a $var there! So that’s what the function sees.
But then the function assigns goodbye! to $var. Windows PowerShell sees that $var still doesn’t exist in the function’s scope, so it creates the variable and puts goodbye! into it. There are now two copies of $var running around: one in the function’s scope and one (which still contains hello!) in the script’s scope. The global scope is still clueless about either of these; there’s no $var in its scope, and it can’t see the variables of its child scopes.
You have seen that scope creates walls between scripts and functions so that each one has space to play—messing up something else. The elements within Windows PowerShell that are affected by scope are variables, functions, aliases, PSDrives, and PSSnapins. We demonstrated that if you create a variable within a script, that variable belongs to that script’s scope. Everything inside that same scope can see that variable and its contents, but the scope’s parent can’t see the variable.
~Don and Jeffrey
Here is the code for the discount offer today at www.manning.com: scriptw7
Valid for 50% off Learn PowerShell Toolmaking in a Month of Lunches and SharePoint 2010 Owner's Manual
Offer valid from April 7, 2013 12:01 AM until April 8, midnight (EST)
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