Understand scopes in PowerShell – Part 1

This blog post covers some of most untalked rules about variable scope in PowerShell. It is untalked because most users never come across it. But it is useful to those who are trying to write a module or new cmdlets in the PowerShell or writing some product support in PowerShell.

It is to be noted that the rules don’t just applies to the variables, but they are also applicable to the aliases, functions, and Windows PowerShell drives (PSDrives). Essentially it controls where they can be read and changed.

Types of Scope in PowerShell

Global: The scope that is in effect when Windows PowerShell starts. Variables and functions that are present when Windows PowerShell starts have been created in the global scope. This includes automatic variables and preference variables. This also includes the variables, aliases, and functions that are in your Windows PowerShell profiles.

Local: The current scope. The local scope can be the global scope or any other scope.

Script: The scope that is created while a script file runs. Only the commands in the script run in the script scope. To the commands in a script, the script scope is the local scope.

Private: 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.

Numbered Scopes: You can refer to scopes by name or by a number that describes the relative position of one scope to another. Scope 0 represents the current, or local, scope. Scope 1 indicates the immediate parent scope. Scope 2 indicates the parent of the parent scope, and so on. Numbered scopes are useful if you have created many recursive scopes.

Basic Rules of Scoping

  • An item you include in a scope is visible in the scope in which it was created and in any child scope, unless you explicitly make it private. You can place variables, aliases, functions, or Windows PowerShell drives in one or more scopes.
  • An item that you created within a scope can be changed only in the scope in which it was created, unless you explicitly specify a different scope.
  • If you create an item in a scope, and the item shares its name with an item in a different scope, the original item might be hidden under the new item. But, it is not overridden or changed.

Let’s try to comprehend that. It will take some time to sink that all in. So everything starts with a globle scope in PowerShell. It is created when you run an instance of PowerShell session. It may also be called as Parent Scope. Now when you run a function or variable directly inside the session, you create child scopes. Each function/variable has its own child scope. If you create variables inside the function, you create further child scopes, for whom the function is a parent scope and the session is grand-parent scope. You can create as many child scopes inside each child scope as you would like to. Do note that the items in a parent scope are always available to be used by a child scope unless they are explicitly restricted. However, items in the parent scope are not inherited by the child scope. They are their to be seen/view and to be changed/modified if required but not more than that. They never become part of the child scope.

Let’s break it down further. We can start a new PowerShell ISE session and see the output of following 2 commands:

# to view the variables in the global scope
Get-Variable -Scope global

# to view the variables in the local scope
Get-Variable -Scope local

You can see the output of these 2 and it is exactly same. Why? It is because the variables in the parent scope are always available to the child scope. Let’s create a local scope variable by using following command:

# Let's create a variable in local scope
$localvar = "this is a variable in the local scope"

Now compare the output of below 2 commands again:

# to view the variables in the global scope
Get-Variable -Scope global

# to view the variables in the local scope
Get-Variable -Scope local

You can see the difference in the output of the 2nd command as below:

Identifying variables in the local scope
Identifying variables in the local scope

If we want to declare a global variable, we would need to declare it like this:

$global:myvar = "this is a global variable"

And again see the output from Get-Variable -Scope global:

identifying-variables-in-the-global-scope
Identifying variables in the global scope

You can explicitly define the scope of the variables using below syntax:

$[]: =

where permissible values to replace [] are Global, Local, Private, and Script. So:

#to define local scope variable

$local:testvar = "this is a local scope variable"

$testvar = "this is a local scope variable"

#to define script scope variable

$script:testvar = "this is a script scope variable"

#to define global scope variable

$global:testvar = "this is a global scope variable"

and so on.

You can also use a scope modifier in functions. The following function definition creates a function in the global scope:

function global:Hello { write-host "Hello World" }

Consider following PowerShell code:

PS C:\Windows\system32> #Declare a global scope variable
$Global:x = 10

function modifyx{
 #this does not modify global variable instead it creates a new local variable
 $x = 9
 $x
}

modifyx

$x
9
10

Above output should be clear now. We can do modify the variables in the parent scope but for that we need to explicitly use their respective scope modifier:

PS C:\Windows\system32> #Declare a global scope variable
$Global:x = 10

function modifyx{
 #this modifies global variable but we need to explicitly specify scope
 $global:x = 9
}

modifyx

$x
9

Same goes for other scopes. Consider below code:

PS C:\Windows\system32> #Declare a function called fun1
function fun1{
 #This declares a local scope variable to function
 $x = "X"
}

fun1

$x

If you have guessed, there is no output generated for $x as it does not exist in the parent scope.

PS C:\Windows\system32> #Declare a function called fun1
function fun1{
 #This declares a local scope variable to function
 $x = "X"
}

#Declare a function called fun2
function fun2{
 fun1
 #This code modifies variable in the fun1
 $x = "Y"
 $x
}

fun2
Y

If you have guessed correctly, the output is Y as shown. Let’s take another example. Declare a local variable $testvar in the current session:

$testvar = “10”

Now, create a script called scope.ps1 and use below commands in the script:

$testvar = 20
Write-Output “The value of `$testvar is $testvar.”

Now, execute the script in the same session. You should be able to see output like this:

The value of $testvar is 20.

Now, again get the value of $testvar and you should be able to see the value is unchanged to 10. Again, this is because the scope of variables inside a script are always limited to the script. Same goes for functions, etc.

Part 2 of this blog post is posted here.

2 thoughts on “Understand scopes in PowerShell – Part 1

  1. Somewhat of an old article so I’m surprised no one has mentioned this yet, as I don’t see any other comments on this article.

    The comment in the last example, “#This code modifies variable in the fun1” is not correct.

    #Declare a function called fun1
    function fun1{
    #This declares a local scope variable to function
    $x = “X”
    }

    #Declare a function called fun2
    function fun2{
    fun1
    #This code modifies variable in the fun1
    $x = “Y”
    $x
    }

    fun2
    Y

    fun2 is not modifying the $x variable from fun1. The $x variable in fun2 is a completely separate and unrelated variable to the variable in fun1. fun2 knows nothing of the $x from fun1. They just happened to be named the same but are two physically separate variables. You might as well have $x in fun1 and $z in fun2. Functionally and scope-wise it’s the same.

    This can be demonstrated by adding a line to fun2 to output $x before attempting to modify it.

    function fun2{
    fun1
    “[$x]”
    #This code modifies variable in the fun1
    $x = “Y”
    $x
    }

    Now run fun2 and you get…
    fun2
    []
    Y

    The brackets are included in the string to clearly show (vs. just a blank line) that $x in fun2 has no value, i.e., doesn’t exist within fun2, after calling fun1.

    Or for an even better illustration…

    function fun2{
    fun1
    $x | Get-Member
    #This code modifies variable in the fun1
    $x = “Y”
    $x
    }

    Again, run fun2…
    fun2
    Get-Member : You must specify an object for the Get-Member cmdlet.
    At line:10 char:6
    + $x | Get-Member

    … because $x doesn’t exist within fun2 immediately after calling fun1.

    Like

Leave a comment