The Powershell "private" scope seems useless at all

I have a script below from the internet:

$private:a = 1 Function test { "variable a contains $a" $a = 2 "variable a contains $a" } test 

He prints 2. No problem. If I remove "private" as shown below:

 $a = 1 Function test { "variable a contains $a" $a = 2 "variable a contains $a" } 

However, he prints "2". There seems to be no difference. Could you provide a quick example of how the "private" area affects the result?

Thanks.

+5
source share
3 answers

A private area can be useful when writing a function that calls a user-provided callback. Consider this simple example:

 filter Where-Name { param( [ScriptBlock]$Condition ) $FirstName, $LastName = $_ -split ' ' if(&$Condition $FirstName $LastName) { $_ } } 

Then, if someone calls it like this:

 $FirstName = 'First2' 'First1 Last1', 'First2 Last2', 'First3 Last3' | Where-Name {param($a, $b) $a -eq $FirstName} 

they expect to see only the line First2 Last2 , but actually it will print all three lines. This is due to a collision with the $FirstName variable. To prevent such collisions, you can declare variables in Where-Name as private:

 filter Where-Name { param( [ScriptBlock]$private:Condition ) $private:FirstName, $private:LastName = $_ -split ' ' if(&$Condition $FirstName $LastName) { $_ } } 

Now $FirstName in the Where-Name does not hide $FirstName in the outer scope if it refers to a $Condition script block.

+10
source

Remarks:
* This answer explains why OP code behaves the way it works (and that it behaves as intended); in addition, it provides some general information about variable areas in PowerShell.
* For an important real use of scope in private see PetSerAl's helpful answer .

Your first piece of code:

 variable a contains variable a contains 2 

Your second print of the fragment:

 variable a contains 1 variable a contains 2 

In the first fragment, the codes, using a set of private reasons of the parent (script), the sphere a variable to be hidden from the child’s sphere (function), as intended, so the first line of the output shows that $a does not matter
(an undefined variable has a value of $null , which evaluates to an empty string in a string context).

In the second fragment, on the contrary, without the private scope modifier, the variable a from the parent scope is visible to the child scope.

In PowerShell, functions are by default executed in child areas.

Thus, in both of the above fragments, assigning the variable a inside the function implicitly creates a local a variable there, whose volume is limited by the containing function.

In other words:

  • Assigning $a to a function creates a local variable of the function called $a , which then hides (hides) the $a script level variable (if it has not already been hidden, being declared as $private:a )
  • When exiting the function, $a again has its initial script level value.

Understanding Variable Areas in PowerShell

  • If the variable is not explicitly hidden in the private scope, private regions can see this variable and read its value using the variable name without the scope qualifier (for example, $a ) or the need for Get-Variable -Scope .

    • Other areas not only do not see the values ​​of private variables by default, but basically cannot refer to them, even with explicit -Scope cross-access using scope modifiers or Get-Variable -Scope .
      (However, in the same scope, you can use the scope modifier to refer to a private variable, but only if this scope modifier effectively targets the same scope, which is always true for $local:privateVarName , $local:privateVarName ).
  • Assigning an unqualified variable, however, implicitly creates a new variable in the current ( local ) region , which can hide a variable with the same name in the ancestral region.

    • That is, $a = 2 implicitly matches $local:a = 2 .
  • To explicitly get / change a variable in the hereditary region, use Get-Variable/Set-Variable -Scope <n> <name> , where <n> represents the level of the region, where 0 represents the current region, 1 parent region, and so on.
    Note that Get-Variable returns an instance of [System.Management.Automation.PSVariable] by default, so to get only the value, access its .Value property or use the -ValueOnly switch, which returns the value just to start with.

    • In functions and interrupt handlers, before creating a local copy of a variable, you can alternatively change the variable in the immediate area of ​​the ancestors, where it is defined as follows:

      • ([ref] $var).Value =...
      • (If one day a local variable with the same name is created, the above will only change the local variable.)
    • Variables in the scope of the script and in the global scope can also be accessed and modified using the $script: and $global: scope modifiers ; e.g. $script:a and $global:a .
      Note that $script: refers to (directly) the top-level area of ​​the script file.

  • Declaring a variable using Set-Variable -Option AllScope allows you to read and modify it in any descendant region without the need for a Set-Variable -Option AllScope name ; in other words: then there is only one variable with that name that any area can directly read and write using the unqualified name of the variable.

    • Without a separate -Scope parameter, -Option AllScope is applied to the variable in the current area (for example, the script area at the top level of the script, the local scope function inside the function). Thus, to safely create a global script variable that can be accessed by unqualified readers and writers, use Set-Variable -Scope Script -Option AllScope .

    • -Scope Global is different from -Option AllScope : while -Scope Global creates a globally accessible variable, the $global: scope modifier is required to read and modify it. Also note that the global variable is a session global , so it is saved even after the script that defined it is complete.

    • By combining -Scope Global with -Option AllScope you effectively create a global singleton session variable that can be read and written from any area without a specifier; as already mentioned, such a variable remains valid even after exiting your script.

+6
source

Good software development means minimizing communication (among other things). In Powershell, which includes the use of private software, EVERY VARIABLE YOU CAN. If you want to make the value available in some subsequently called module, pass this information EXPLICITLY. There must be a very good EXCLUSIVE reason not to do this, because every time you rely on implicit knowledge (such as the view that happens in Powershell when you are not using private variables), you increase the likelihood that something will happen unexpectedly wrong later (maybe a few months later when the software has a lot more code).

-one
source

All Articles