Join Don Jones for an in-depth discussion in this video Understand and use scope, part of PowerShell 3.0 Scripting and Tool Making.
- We're going to start with some overview of basic scripting concepts in Windows PowerShell. None of these are things that you're necessarily going to use in every single script that you write or every single command you create, but they're important background concepts that will affect just about everything you do. The first thing we're going to start with is the concept of scope. Scope is a system of containerization, and it's really easy to draw a lot of pictures to illustrate it but it's more effective, I think, to see it in action.
So let's just create a very, very simple script. I'm going to ask the script to start by displacing the contents of a variable called test. I'm very deliberately using right host here instead of right output. I just want this information contained on the screen. After that I'll ask it to change the contents of test to 100, and then write that out again, just to verify that it actually did something. To make this a little bit more robust we'll call it try 1.
We'll put this in double quotation marks so that the variable test will get replaced with its contents. That way if test winds up being empty, we'll still see the text try 1 and try 2. Let's save this. We'll just call it test, and I'm going to drop down to the command line. Right here I want to make sure that there is no variable called $test. It doesn't look like there is, but it could still exist and be empty. So let's double check by looking at the variable drive, And no, I do not see any variables called test anywhere here.
These are alphabetic, so it's pretty easy to find. So now let's run that test script. You can see that on try 1 test was empty. Now that makes sense, it was at the very beginning of the script and we hadn't put anything in there. And now at the end of the script it equals 100, which makes sense because that's what we put into it. Here in the shell let's just look and see if $test has anything in it. Nope, it doesn't and it does not exist in the variable drive. So here's what happened.
Test does not exist here in the global shell. When I ran this script a new scope was created, a new container was created. Inside that container we tried to access the contents of test. PowerShell said well, test doesn't exist here in the script and so it then went up to the global scope, the shell to see if test existed there. It did not, and so it gave test a default value of nothing, an empty string. We then put 100 into test and displayed it again.
This time PowerShell was able to say yep, I know where test is, it's right there, and it contains 100. And so that's why it displayed what it did, but that didn't affect anything here at the global scope. Now let's make a slight change. We're going to set test equal to 10 here in the global scope. Let's just check to make sure it's there, and in our variable drive we can see that test does indeed contain 10 here in the global scope. Now let's run that script again. This time try 1 had 10 in it.
Here's what happened. PowerShell ran the script and we said, well show me what's inside of test. It said, uh, there is no test in this scope, so I will go up to your parent scope, to the global scope, and there I find that test equals 10. So that's what we'll display. Then it changed test to 100, and that's why it displayed as 100 the second time. Let's see what it equals here in the shell. It's still 10, and that is potentially one of the most confusing things about PowerShell scripting.
We've got two different scopes going on here. The global scope, which is the shell, and then, while this script was executing we had a script scope. Let's walk through exactly what PowerShell did. It started by running the script. The first line in the script attempted to access the variable test. It doesn't exist inside of this scope, and so PowerShell went up to the global scope and found that test equalled 10, and so that's what was displayed for try 1, then test was set to 100.
This created a new variable called it test inside the script scope and set it to 100, and that's why try 2 displayed 100. Once the script finished executing it's copy of test went away completely. The global version of test was never changed at all. We never had any impact on that. So this idea of being able to retrieve information from a parent scope, you can read it, but you cannot change the information in your parent scope, is one of the fundamentally, I think, confusing things about scope in PowerShell.
You can read a lot more about scope, and I certainly recommend you do, in the "help about scopes" help topic. This walks through all the different rules as well as the rules for deliberately changing your parent scope. Let's take a look at that. I've made a minor adjustment to the script. Let's save it, go out to the console, we see that test contains 10, and now we're going to run the test script.
The output was the same, meaning in the first line of the script it was retrieving the value from the global scope. The second line of the script set the global test variable to 100, and line three displayed that. And now in the shell test has been changed to 100 because line two of the script explicitly changed the variable in the script's parent, which is the global scope. Kind of a poor practice. If you can imagine running a script that is modifying your shell which might affect other scripts, it's a bad idea, but you will see folks use that technique and so it's important to understand what's happening there.
If this script had contained a function, everything inside this function is yet another scope. So this is a scope, which is a child of the script, which is a child of the global. So it creates a sort of hierarchy of relationships. By default, any item can be read from your parent, but if you try to modify something, what you're really doing is creating a new copy inside your own scope, unless you use this explicit scope syntax.
You do need to be really, really careful when you're modifying out of scope information, and as a best practice avoid doing so entirely. There are proper ways to pass information into a scope, whether it's a script or a function, and there are proper ways to get information out of those, and that's what we're going to be reviewing as we progress through the scripting and tool making topics.