Variable Scopes Within PowerShell Nested Functions

slybloty picture slybloty · Apr 23, 2015 · Viewed 8.9k times · Source

I was given this extensive PowerShell script from a major/reputable corporation which was supposed to work flawlessly. Well, it didn't.
The script is composed of numerous nested functions with lots of variables passed to the main parent function and then to all of its children. Children who use and modify all these variables.

Why is it that all these variables do not contain the correct data?
Here's the structure I'm talking about:

f1 {
     f2 {
          v #prints 0
          v = 1
          f3
     }
     f3 {
          v #prints 1
          v = 2
     }
     v = 0
     f2
     v #should print 2 but prints 0
}

Answer

slybloty picture slybloty · Apr 23, 2015

Within nested functions, all child functions have access to all of the parent functions' variables. Any changes to the variables are visible within the local scope of the current function and all the nested child functions called afterwards. When the child function has finished execution, the variables will return to the original values before the child function was called.

In order to apply the variable changes throughout all the nested functions scopes, the variable scope type needs to be changed to AllScope:

Set-Variable -Name varName -Option AllScope

This way, no matter at what level within the nested functions the variable gets modified, the change is persistent even after the child function terminates and the parent will see the new updated value.


Normal behavior of variable scopes within Nested Functions:

function f1 ($f1v1 , $f1v2 )
{
        function f2 ()
       {
               $f2v = 2
               $f1v1 = $f2v #local modification visible within this scope and to all its children
               f3
               "f2 -> f1v2 -- " + $f1v2 #f3's change is not visible here
       }
        function f3 ()
       {
               "f3 -> f1v1 -- " + $f1v1 #value reflects the change from f2
               $f3v = 3
               $f1v2 = $f3v #local assignment will not be visible to f2
               "f3 -> f1v2 -- " + $f1v2
       }

        f2
        "f1 -> f1v1 -- " + $f1v1 #the changes from f2 are not visible
        "f1 -> f1v2 -- " + $f1v2 #the changes from f3 are not visible
}

f1 1 0

Printout:

f3 -> f1v1 -- 2
f3 -> f1v2 -- 3
f2 -> f1v2 -- 0
f1 -> f1v1 -- 1
f1 -> f1v2 -- 0

Nested Functions with AllScope variables:

function f1($f1v1, $f1v2)
{
    Set-Variable -Name f1v1,f1v2 -Option AllScope
    function f2()
    {
        $f2v = 2
        $f1v1 = $f2v #modification visible throughout all nested functions
        f3
        "f2 -> f1v2 -- " + $f1v2 #f3's change is visible here
    }
    function f3()
    {
        "f3 -> f1v1 -- " + $f1v1 #value reflects the change from f2
        $f3v = 3
        $f1v2 = $f3v #assignment visible throughout all nested functions
        "f3 -> f1v2 -- " + $f1v2 
    }

    f2
    "f1 -> f1v1 -- " + $f1v1 #reflects the changes from f2 
    "f1 -> f1v2 -- " + $f1v2 #reflects the changes from f3 
}

f1 1 0

Printout:

f3 -> f1v1 -- 2
f3 -> f1v2 -- 3
f2 -> f1v2 -- 3
f1 -> f1v1 -- 2
f1 -> f1v2 -- 3