Powershell “private” scope seems not useful at all

后端 未结 3 479
一向
一向 2020-11-27 07:45

I\'ve got the script below, from internet:

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


        
3条回答
  •  佛祖请我去吃肉
    2020-11-27 08:15

    Note:
    * This answer explains why the OP's code behaves the way it does (and that it behaves as designed); additionally, it provides some general information about variable scopes in PowerShell.
    * For an important real-world use of scope private, see PetSerAl's helpful answer.

    Your first snippet prints:

    variable a contains
    variable a contains 2
    

    Your second snippet prints:

    variable a contains 1
    variable a contains 2
    

    In the first snippet, using scope private causes the parent (script) scope's a variable to be hidden from the child (function) scope, as designed, so the first output line shows that $a has no value
    (an undefined variable has value $null, which evaluates to the empty string in a string context).

    In the second snippet, by contrast, without the private scope modifier, variable a from the parent scope is visible to the child scope.

    In PowerShell, functions execute in child scopes by default.

    Therefore, in both snippets above, assigning to variable a inside the function implicitly creates a local a variable there, whose scope is limited to the enclosing function.

    In other words:

    • Assigning to $a in the function creates a function-local variable named $a, which then shadows (hides) the script-level $a variable (if it wasn't already hidden by having been declared as $private:a) - though note that local in PowerShell means that child scopes do see its value; see next section.
    • On leaving the function, $a again has its original, script-level value.

    General information about variable scopes in PowerShell

    • Unless a variable is explicitly hidden with scope private, descendant scopes can see that variable and read its value using the variable name without a scope qualifier (e.g., $a) or the need for Get-Variable -Scope.

      • Note that while descendant scopes do not see the values of variables created with $private: by default, they can still refer to them with relative cross-scope access, using Get-Variable -Scope or Set-Variable -Scope.
        Non-relative scope modifiers ($script, $global, $local) generally do not work - except if the reference happens in the same scope in which the private variable was created and the scope modifier happens to effectively target that same scope, which is always true for $local:privateVarName, for instance.
    • Assigning to an unqualified variable, however, implicitly creates a new variable in the current (local) scope, which can shadow a variable of the same name in an ancestral scope.

      • That is, $a = 2 is implicitly the same as $local:a = 2.
    • To explicitly get / modify a variable in an ancestral scope, use Get-Variable / Set-Variable -Scope , where represents the scope level, with 0 representing the current scope, 1 the parent scope, and so on.
      Note that Get-Variable returns a [System.Management.Automation.PSVariable] instance by default, so in order to get only the value, access its .Value property, or use the -ValueOnly switch, which only returns the value to begin with.

      • In functions and trap handlers, before creating a local copy of a variable, you can alternatively modify a variable in the most immediate ancestral scope where it is defined as follows:

        • ([ref] $var).Value = ...
        • (If and once a local variable by the same name is created, the above will modify only the local variable, however.)
      • Variables in the script scope and the global scope can also be accessed - and modified - by using the $script: and $global: scope modifiers; e.g., $script:a and $global:a.
        Note that $script: refers to the (immediately) enclosing script file's top-level scope.

      • Modules each have their own scope domain linked only to the global scope. That is, modules see outside variables only from the global scope, not from a caller in any other scope, such as from a script; this can cause unexpected behavior with preference variables, as discussed in this GitHub issue.

    • Declaring a variable with Set-Variable -Option AllScope allows it to be read and modified in any descendant scope without needing to qualify the name; to put it differently: only a single variable by that name exists then, which any scope can directly read and write using the unqualified variable name.

      • Without a separate -Scope parameter, -Option AllScope is applied to the variable in the current scope (e.g., the script scope at the script's top level, a function's local scope inside a function). Thus, to safely create a script-global variable that you can access unqualified for reading and writing, use Set-Variable -Scope Script -Option AllScope.

      • -Scope Global is distinct from -Option AllScope: while -Scope Global creates a globally accessible variable, reading it may, and modifying it does, require the $global: scope modifier. Also note that a global variable is session-global, so it persists even after the script that defined it has terminated.

      • By combining -Scope Global with -Option AllScope you effectively create a session-global singleton variable that can be read and written from any scope without qualifier; as stated, however, such a variable lives on even after your script exits.

提交回复
热议问题