Tag Archives: scope

Scoping Out the Scope

Scope is awesome; it really is. In my mind, it is a protectionary layer to ensure things like variables are somewhat protected from say, modification. It can become a necessary concept for people using Powershell to understand. As I worked through some ideas recently, I ended up writing a function — this has been known to happen. As I sat there and fully comprehended what I had written, I began to realize that it may be worth sharing, so others can see it too. Sometimes we just need a simple example and a corresponding explanation. That has kind of been my goal over the years. Anyway, let’s focus on this function and I’ll explain what it is we have here and what it is doing.

The function is named, Get-TheVariable and it contains four commands and four comments, although two of those comments are just dashed lines. I don’t usually write comments anything like this, but I really want to break up the commands into two segments, to make sure I am as clear as I can be, as we walk through this, together. Those segments are the Global scope and the Local scope, but we will get to that soon enough. Down beneath the function are two separate commands separated by a semi-colon. The first command just clears the screen — because sometimes I need that to stay clear-headed — and the second invokes the Get-TheVariable function. Have a peek at the function now and then we will discuss it further below.

function Get-TheVariable {
    # Set and then get the GLOBAL version of the "Variable1" variable.
    # ----------------------------------------------------------------
    Set-Variable -Name Variable1 -Value 'G' -Scope Global -Description 'Scope:Global'
    Get-Variable -Name Variable1 -Scope Global | Select-Object -Property Name,Value,Description

    # Set and then get the LOCAL version of the "Variable1" variable.
    # ---------------------------------------------------------------
    Set-Variable -Name Variable1 -Value 'L' -Scope Local -Description 'Scope:Local/Function'
    Get-Variable -Name Variable1 -Scope Local | Select-Object -Property Name,Value,Description
}

Clear-Host; Get-TheVariable

So what is happening here? Before we go there, know something about how Set commands usually work. They work like New commands, unless whatever we are to trying to create already exists. In that case, they just make modifications.

The first two commands create, or modify a variable (if it already exists) and then return the variable. It is named “Variable1,” its value — like what it stores — is “G,” it is globally scoped, and it includes a description indicating that. It is created by the function, but not inside the function. It is created outside of the function, in the global scope.

The second two commands kind of do the same things, They create, or modify a variable (if it already exists) and then return the variable. It is named “Variable1,” its value — what it stores — is “L,” it is locally scoped, and it includes a description indicating that. It is also created by the function, but it is inside of the function. It is created inside of the function, in the local scope. While these variables have many things in common, such as their name, they are different.

In the end — and sorry for all the repetition — there are going to be two variables with the same name. This is perfectly okay since they are going to be created in different scopes. In real life, I would recommend not using the same name for variables even if they are scoped differently. For this example, and for fully understanding this concept, it is important we do it this way.

The below output is created by our function. In the first line, we have information about our globally scoped “Variable1” variable that was created by the function, outside of the function. Because it is globally scoped it is going to persist outside of the function. In the second line, we have information about our locally scoped “Variable1” variable that was created by the function, inside the function. Because it is locally scoped, it is not going to persist outside the function.

During the invocation of the function, the function’s scope is the local scope. When the function’s invocation is done and over, the local scope is the global scope (again). The local scope is always the current scope.

Name      Value Description
----      ----- -----------
Variable1 G     Scope:Global
Variable1 L     Scope:Function

When the function has ended, the locally scoped “Variable1” variable stops existing. The global version, however, is still alive and well. Let’s prove it.

Get-Variable -Name Variable1 -Scope Global | Select-Object -Property Name,Value,Description
Name      Value Description 
----      ----- ----------- 
Variable1 G     Scope:Global

In the above example, using -Scope Global was not necessary. Why? It’s because, again, we’re in the global scope and so there’s no need to tell the command to check a scope we are already in.

But, look at this.

Get-Variable -Name Variable1 -Scope Local | Select-Object -Property Name,Value,Description
Name      Value Description 
----      ----- ----------- 
Variable1 G     Scope:Global

What, the local variable still exists!? Uh, no, not the one created by the function for inside the function. This is kind of a recap — maybe the third or fourth one now, who knows. Again, now that we are outside of the function and back in the global scope, the Local parameter value returns the globally scoped variable, too. See the Value and Description properties? They are the same as the global variable because the local variable is the global variable when we are in the global scope.

I am beginning to think I should have left this topic to someone else. Read it a few times and if maybe I have confused you — ask questions, Read more on about_Scopes. I feel good about this post, I just likely need to read it like 10 more times and ensure it mostly makes sense.

I do want to mention this real quick because it comes up often, and good for you for reading this far; it may just pay off. If you are invoking a function and you reference a variable that has not been created or defined in the function, PowerShell will look for it outside of its local scope. That’s to say that it will go looking for the variable you didn’t assign, in the global scope, also known as the parent scope. And with the introduction of that new term, the function’s scope has another name, too. It is the child scope. Okay, I’m done. Hopefully, this didn’t confuse anyone. Phew.

Functions Finding Variable Values

Every once in awhile I forget something I know, I know. Not sure why, but for a quick moment, I suddenly couldn’t remember which can access what. Can a nested, or child, function, access the variable in the containing, or parent, function, or is it the other way around? That is, can a containing, or parent, function access the variable in the nested, or child, function? Even as I write this, I still can’t believe that for a moment I forgot what I’ve known for so long.

Duh. A nested or child function can access a variable assigned in the containing or parent function. If the child function cannot find a declared variable inside itself, it’ll go upward in scope to its mom or dad, and ask if they have the variable assigned, and if so, they can borrow it. Being good parents, they offer it if they have it.

It’s so obnoxious that in a quick moment, I wasn’t sure, even though I’ve pictured it in my own head multiple times, as well as relied on it. Anyway, here’s an example that does prove that a nested function will go upward to find a variable, if it’s not been assigned inside itself.

Function AAA {
    'In function AAA.'
    Function BBB {
        'In function BBB.'
        $x = 5
    }
    $x
    BBB
}
AAA

Function CCC {
    'In function CCC.'
    $x = 5
    Function DDD {
        'In function DDD.'
        $x
    }
    DDD
}
CCC
In function AAA.
In function BBB.
In function CCC.
In function DDD.
5

This got me wondering, how far up will it go!? I assume it’ll go up and up and up, and well I was right. Take a look at this example. I guess if I had to forget something so simple that at least I got an opportunity to build this example. Enjoy the weekend!

Function Top {
    $x = 10
    "0. Assigned `$x with $x."
    "1. Inside the $($MyInvocation.MyCommand.Name) function."
 
    Function MidTop {
        "2. Inside the $($MyInvocation.MyCommand.Name) function."
 
        Function MidBottom {
            "3. Inside the $($MyInvocation.MyCommand.Name) function."
 
            Function Bottom {
                "4. Inside the $($MyInvocation.MyCommand.Name) function."
                If (Get-Variable -Name x -Scope Local -ErrorAction SilentlyContinue) {
                    '5. Can find the variable in the local scope.'
                } Else {
                    '5. Cannot find the variable in the local scope.'
                }
                "6. The value of `$x is $x."
            } # End Function: Bottom.
            Bottom
 
        } # End Function: MidBottom.
        MidBottom
 
    } # End Function: MidTop.
    MidTop
 
} # End Function: Top.
Top
0. Assigned $x with 10.
1. Inside the Top function.
2. Inside the MidTop function.
3. Inside the MidBottom function.
4. Inside the Bottom function.
5. Cannot find the variable in the local scope.
6. The value of $x is 10.

Update: I made a few modifications to the above function. Now, it indicates that it cannot find the $x variable in the local scope of the Bottom function. That’s one way to help prove it goes upward through the parent functions looking for a value for $x.