Scope in PowerShell

PowerShell variables and functions have scope. The “scope” is what defines the visibility of that variable or function.

For instance, consider the following script:

If you were to run this script, here’s the output:

As you can see, the variable x was set as 2 initially but when the function blah set it to 5 the change didn’t really affect it. This is because of scopes. What happens is that the script variable x belongs to a different scope from the function variable x and although both have the same name they are in fact two different beings. In a way it’s like the variables are in two different boxes – each script/ function has a box full of variables, and whatever change they make to the variables in their box doesn’t affect the variables in other boxes. This is the case even if a function is within a script or another function – the child-function has a box of variables independent of its parent.

Now, the concept of scopes only matter when you are making changes to a variable. If you were just reading the value of a variable then scope doesn’t matter much. Consider the following script:

Here even though the function blah has a box of its own variables, when we refer to variable x within it since the variable is not present in its box it looks up to the box of its parent and reads the value from there. Hence the output of the script is:

But suppose we had made a change to variable x within the function (as in the first case), then a new variable x is created within the scope of that function and whatever change we make is limited to that variable.

There are four types of scopes:

  1. Local scope: the box you have, the current scope basically of the script or function you are in.
  2. Global scope: the big box that’s out there, the scope containing all variables that are in your particular PowerShell session (be it a PowerShell window or ISE)
  3. Script scope: another big box, but not as big as the global scope and not as limited as the local scope, this is the scope containing all variables that are in your particular PowerShell script session. The script scope is like a global scope for scripts.
  4. Private scope: a secret box you have, containing variables you don’t want your children to see. Remember I said earlier that if a variable is not present in a function it looks to the parent scope and reads it from there? If you have any variables you don’t want such children functions to see, define them in a private scope.

Consider the following script:

This is just to illustrate the first three scopes mentioned above. Here’s the output from the script:

The variable x was set as 2 initially. Function blah changed it to 5, but that only took effect in the local scope of the function and so x was still 2. Next, function blah2 changed x to 5 in the global scope (by addressing the variable as $global:x), but that too didn’t change the value of x as the change was made in the global scope and not the script scope. Finally, function blah3 changed x to 5 in the script scope (by addressing it as $script:x) and that effected the change.

From this example one can also see the need for a script scope and a global scope. Function blah3 changed variable x globally within the script, but any variable x existing outside the script and in the PowerShell session was unaffected. Similarly function blah2 changed variable x within the PowerShell session of the script, but the variable x within the script itself was unaffected (of course, if the script didn’t have a variable x then any references to x will pick up the variable from the global scope). If we didn’t have separate scopes for scripts and the PowerShell session, all scripts would mess about with the global scope variables.

Note though: The global scope and script scope are separate only if the script is run and not dot-sourced. If a script is dot-sourced, the script scope becomes the global scope.

And lastly, private scopes. Consider this script:

And here’s the output:

Since y was defined with a private scope in the script, function blah can’t read its value.

Scopes can also be referred to by a number. 0 is the local scope, 1 is the parent scope, 2 is the grandparent scope, and so on. This is useful with the Get-Variable cmdlet, for instance. Consider the following script:

Its output is thus:

As you can see, there are only two scopes – 0 is the local scope (the script scope in this case), 1 is the global scope. If I had defined a function within the script, then scope 0 would be the local scope, 1 would be the script scope, and 2 would be the global scope.

Accessing scopes by number is also useful with cmdlets such as Set-Variable. For instance, the following sets a variable in the parent scope of the calling script:

That’s all for now. It’s worth reiterating that within a script you have a global scope and a script scope, and the two are separate. Setting a variable in the global scope from a script does not affect the variable in the script scope.