Scope of variables

The different scopes for the variables used in Powershell

Scope of variables

The different scopes for the variables used in Powershell

Information

This article was written for a presentation of the French Powershell UserGroup.

This presentation can be seen on Youtube on the FRPSUG channel

The scope of variables in Powershell

Several scopes are possible for the use of variables according to need

Some basic rules:

  • An element that 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
  • If you create an element in a scope and the element shares its name with an element in another scope, the original element can be hidden under the new element. However, it is not replaced or modified.

Attention at the end of the article we will talk about a particular case occurring in the PowerShell modules.

Global

variables of this type are declared as follows:

$global:MaVariable

The variables defined in this scope have visibility extended to ALL the scripts that run at that time but also after their end of execution.

Let's take a script that only defines a global variable MyVariable

$Global:MaVariable = "My bank card number :-) "

Then launch a second script with a function that will read this variable (noted although the previous script is finished)

function DoSomething {
    Clear-Host
    Write-Output "I am in DoSomething !"
    Write-output $Global:MaVariable
}

DoSomething

The result is

I am in DoSomething !
My bank card number :-)

This variable will continue to exist until you leave the Powershell context in which you created it.

For example, the variables present at the start of Powershell, such as automatic variables and preference variables, are in this scope so that they can be used everywhere.

Local

Local scope is the default scope, a variable without modifier is created or referenced in the Local scope. Local variables are those that do not specify modifier and those that specify the Local or Private modifier.

You will find an example in the next point on the scope Script

Script

variables of this type are declared as follows:

$script:MaVariable

It is a range created during the execution of a script and which disappears with the end of the execution of the script.

The variables defined in this scope are visible only in the script and during the execution of the script.

function DoSomething {
    $Script:MaVariable = "Created in scope script"
    $MaVariable1 = "Created in the scope of the function"
    Clear-Host
    Write-Output "I am in the function"
    $Script:MaVariable
    $MaVariable1
}

DoSomething

Write-Output "I am in the script"
$Script:MaVariable
$MaVariable1

Le r├ęsultat est

I am in the function
Created in scope script
Created in the scope of the function
I am in the script
Created in scope script

The Variable $MyVariable1 is a variable local to the function DoSomething so it is not visible in the scope of the script unlike the variable $Script:MyVariable

Private

$private:MaVariable

Items in private scope cannot be seen outside of the current scope. You can use a private scope to create a private version of an item with the same name in another scope.

$MaVariable = "my email address !"

function DoSomething {
    $Private:MaVariable = "My bank card number !"

    function DoSomething2 {
        Write-Output "I'm in DoSomething2 and MyVariable is worth $MaVariable"
    }
    DoSomething2
}
DoSomething

Le r├ęsultat est

I'm in DoSomething2 and MyVariable is worth my email address  !

Because the variable $MyVariable in the DoSomething function is it as private, it is not visible in the DoSomething2 function which therefore uses the variable $MyVariable defined at the beginning of the script in the Local scope

Numeroted

You can refer to staves by their name or by a number describing the relative position of a field with the other.

Scope 0 represents the current or local scope.

Scope 1 indicates the immediate parent scope.

Scope 2 indicates the parent of the parent scope, etc.

Numbered scopes are useful if you have created many recursive scopes.

Let's take the following script

Clear-Host
$x = "testvar"

function fun1{
    Write-Output "Fun1"
    Write-Output "The inheritance makes it possible to recover the value of X in the function Fun1 => $x"
}

fun1

function fun2{
    Write-Output ""
    Write-Output "Fun2"
    Set-Variable -Name x -Value "testvar2" -Scope 0
    Write-Output "the scope 0 notation modifies the value of X in this function => $x"
    Write-Output "Scope 1 notation retrieves the value of the variable in the parent => $((get-variable -Name X -Scope 1).Value)"
}

fun2

function fun3{
    Write-Output ""
    Write-Output "Fun3"
    Set-Variable -Name x -Value "testvar3" -Scope 1
    Write-Output "the scope 1 notation modifies the value of X in the parent function => $x"
    Write-Output "Scope 1 notation retrieves the value of the variable in the parent => $((get-variable -Name X -Scope 1).Value)"
}

fun3

the result is as follows

Fun1
The inheritance makes it possible to recover the value of X in the function Fun1 => testvar

Fun2
the scope 0 notation modifies the value of X in this function => testvar2
Scope 1 notation retrieves the value of the variable in the parent => testvar

Fun3
the scope 1 notation modifies the value of X in the parent function => testvar3
Scope 1 notation retrieves the value of the variable in the parent => testvar3

Special case: The modules


In the case of modules there is a subtlety in terms of variables in the scope Script.

A script variable defined in a module is accessible by all the functions, scripts making up the module, but also from the context of the script which led to the creation of this variable.

It sounds complicated, but let's see with a small example of what it is all about.

Imagine the next module : Demo5_ModuleScope.psm1

function Set-MrVar {
    $PsProcess = Get-Process -Name PowerShell
}
function Set-MrVarLocal {
    $Local:PsProcess = Get-Process -Name PowerShell
}
function Set-MrVarScript {
    $Script:PsProcess = Get-Process -Name PowerShell
}
function Set-MrVarGlobal {
    $Global:PsProcess = Get-Process -Name PowerShell
}
function Test-MrVarScoping {
    if ($PsProcess) {
        Write-Output $PsProcess
    }
    else {
        Write-Warning -Message 'Variable $PsProcess not found!'
    }
}

It is quite simple it creates a variable $PsProcess in the various scope that we saw previously.

To call this module let's do a little script : Demo5_ModuleScope.ps1

#Import module Demo5_ModuleScope.psm1

#Definition of the variable without constraint of the scope in a function of the module
Set-MrVar

#Check the value of the $PsProcess variable from another function of the same module
Test-MrVarScoping

#Check the value of the $PsProcess variable from the current scope
$PsProcess

#Set the variable to local scope from a function of the module
Set-MrVarLocal

#Check the value of the $PsProcess variable from another function of the same module
Test-MrVarScoping

#Check the value of the $PsProcess variable from the current scope
$PsProcess

#Set the variable to the script scope from within a function in the module
Set-MrVarScript

#Check the value of the $PsProcess variable from another function of the same module
Test-MrVarScoping

#Check the value of the $PsProcess variable from the current scope
$PsProcess

#Set the variable to the global scope from a function of the module
Set-MrVarGlobal

#Check the value of the $PsProcess variable from another function of the same module
Test-MrVarScoping

#Check the value of the $PsProcess variable from the current scope
$PsProcess

This script executes in turn the creation of the variable $PsProcess in each of the scopes, and checks if the variable is visible in the context of the module (psm1) and / or of the script (ps1)

In most cases the scope behaves exactly as it should except for the scope Script.

We start by instantiating the scope Script variable

Set-MrVarScript

Then let's test the value of this variable from the Test-MrVarScoping function so we are in the context of the module

PS > Test-MrVarScoping

Handles  NPM(K)    PM(K)      WS(K)     CPU(s)     Id  SI ProcessName
-------  ------    -----      -----     ------     --  -- -----------
    599      27    65608      74188       0,83   1580   2 powershell
    783      41   135132     133900       6,59   9564   2 powershell

Logically we have a return since the variable is in the Script scope and the Test-MrVarScoping function is also in the same file.

Let's try the same thing in the context of the script Demo5_ModuleScope.ps1

PS > $PsProcess

Handles  NPM(K)    PM(K)      WS(K)     CPU(s)     Id  SI ProcessName
-------  ------    -----      -----     ------     --  -- -----------
    599      27    65608      74188       0,83   1580   2 Powershell
    783      41   135132     133900       6,63   9564   2 Powershell

Surprising :-)

Indeed although defined as a script in the module, the variable $PsProcess is also available in the context of the ps1 script which uses this module.

Here I hope to have taught you a little more about this notion of scope of variables in Powershell.


See also