Powershell"私人"范围似乎毫无用处

vik*_*ata 6 powershell scope private

我从下面得到了以下脚本:

$private:a = 1
Function test  {
    "variable a contains $a"
    $a = 2
    "variable a contains $a"
}
test
Run Code Online (Sandbox Code Playgroud)

打印2.没问题.如果我删除"私人",如下所示:

$a = 1
Function test  {
    "variable a contains $a"
    $a = 2
    "variable a contains $a"
}
Run Code Online (Sandbox Code Playgroud)

仍然打印"2".似乎没有区别.您能否快速提供"私人"范围如何影响结果的样本?

谢谢.

use*_*407 12

在编写调用用户提供的回调函数时,私有作用域非常有用.考虑这个简单的例子:

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

然后,如果有人这样称呼它:

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

他们期望只能看到First2 Last2行,但实际上这将打印所有三行.这是因为$FirstName变量发生了碰撞.要防止此类冲突,您可以将变量声明Where-Name为私有:

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

现在,$FirstNameWhere-Name不隐藏$FirstName在引用时在外部范围$Condition脚本块.


mkl*_*nt0 8

注意:
*这个答案解释了为什么 OP的代码行为方式(并且它的行为与设计一样); 此外,它还提供了有关 PowerShell中变量范围的一些常规信息.
*对于一个重要的现实世界中使用的范围private,见 PetSerAl的有用的答案.

你的第一个片段打印:

variable a contains
variable a contains 2
Run Code Online (Sandbox Code Playgroud)

你的第二个片段打印:

variable a contains 1
variable a contains 2
Run Code Online (Sandbox Code Playgroud)

在第一片段中,使用范围private会导致母体(脚本)范围的a变量将被隐藏从子(功能)范围,因为设计的,所以在第一输出线显示,$a具有没有
(未定义的变量具有值$null,其评估对字符串上下文中的空字符串).

相反,在第二个片段中,如果没有private范围修饰符,a则父范围内的变量对子范围可见.

在PowerShell中,函数默认在作用域中执行.

因此,在上面的两个片段中,为函数内部的变量赋值a隐式地在那里创建一个局部 a变量,其范围仅限于封闭函数.

换一种说法:

  • 分配$a 的函数创建一个函数本地命名变量$a,然后阴影(隐藏)的脚本级 $a变量(如果它是不是已经被被宣称为隐藏$private:a)
  • 离开函数时,$a再次具有其原始的脚本级值.

有关 PowerShell中变量范围的一些常规信息:

  • 除非使用范围显式隐藏变量,否则private后代范围可以看到该变量并使用变量名称读取其值而不使用范围限定符(例如$a)或需要Get-Variable -Scope.

    • 其他范围默认情况下不会看到私有变量的值,但从根本上不能引用它们,即使使用范围修饰符或显式跨范围访问也是如此Get-Variable -Scope.
      (但是,在相同的范围内,您可以使用范围修饰符来引用私有变量,但前提是该范围修饰符有效地针对同一范围,例如,总是如此$local:privateVarName).
  • 分配到一个不合格的变量,但是,隐式地创建一个的可变电流(local)范围,其可以遮蔽在祖先范围相同的名称的变量.

  • 要显式获取/修改祖先范围中的变量,请使用$a = 2 where $local:a = 2表示范围级别,并Get-Variable / Set-Variable -Scope <n> <name>表示当前范围,<n>父范围等.
    请注意,默认情况下0返回一个1实例,因此为了只获取,访问其Get-Variable属性或使用[System.Management.Automation.PSVariable]switch,它只返回值以开始.

    • 函数陷阱处理程序中,创建变量的本地副本之前,您也可以修改最直接的祖先范围中的变量,其中定义如下:

      • .Value
      • (如果创建一个同名的局部变量,上面的内容只会修改局部变量.)
    • 通过使用-ValueOnly([ref] $var).Value = ...范围修饰符,还可以访问和修改脚本范围和全局范围中的变量 ; 例如,$script:$global:.
      注意,$script:a指的是(立即)封闭脚本文件的顶级范围.

  • 声明一个变量$global:a允许在任何后代范围内读取和修改它,而无需限定名称 ; 换句话说:只存在该名称的单个变量,任何范围都可以使用非限定变量名直接读写.

    • 如果没有单独的$script:参数,Set-Variable -Option AllScope则应用于当前作用域中的变量(例如,脚本顶级的脚本作用域,函数内部函数的本地作用域).因此,为了安全地创建一个脚本 -global变量,您可以访问不受限制的读取写入,请使用-Scope.

    • -Option AllScope有别于Set-Variable -Scope Script -Option AllScope:当-Scope Global创建一个全局可访问变量,读它可能,并修改它确实,所需要的-Option AllScope范围改性剂.另请注意,全局变量是session -global,因此即使在定义它的脚本终止后它仍然存在.

    • 通过-Scope Global$global:您结合,有效地创建一个会话 -全局单例变量,可以从任何范围读取和写入,不带限定符; 然而,如上所述,即使在您的脚本退出之后,这样的变量依然存在.