Powershell“私人”范围似乎毫无用处

时间:2016-04-01 02:01:24

标签: powershell scope private

我从互联网上获得了以下脚本:

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

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

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

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

感谢。

3 个答案:

答案 0 :(得分:11)

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

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

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

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

他们希望只看到First2 Last2行,但实际上这会打印所有三行。 这是因为$FirstName变量发生冲突。 为防止此类冲突,您可以将Where-Name中的变量声明为私有:

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

$FirstName脚本块引用时,Where-Name中的$FirstName现在$Condition不会隐藏$sql_qry_group = "SELECT country_code, Count(users) as userscnt FROM `users` GROUP by country_code "; $qry_users_group = mysqli_query($db_conn, $sql_qry_group); while($users_country = mysqli_fetch_assoc($qry_users_group)) { $countries[] = $nodes_country_set; }

答案 1 :(得分:7)

注:
*这个答案解释了为什么 OP的代码行为方式(并且它的行为与设计一样);另外,它在PowerShell中提供了一些关于变量范围的一般信息 *对于范围private重要的实际使用,请参阅PetSerAl's helpful answer

您的第一个代码段打印:

variable a contains
variable a contains 2

你的第二个片段打印:

variable a contains 1
variable a contains 2

在第一个代码段中,使用范围private会导致父(脚本)范围的a变量从子(函数)范围隐藏,如设计的那样,第一个输出行显示$a具有
(未定义的变量具有值$null,其值为字符串上下文中的空字符串)。

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

在PowerShell中,默认情况下,函数在作用域中执行。

因此,在上面的两个片段中,分配给函数内的变量a会隐式创建 local {{1}变量那里,其范围仅限于封闭函数。

换句话说:

  • 在函数中将分配给a 会创建一个名为$a function-local 变量,然后< em> shadow (隐藏)脚本级 $a变量(如果它已被声明为$a而未被隐藏) - 尽管请注意,PowerShell中的 local 意味着范围看到它的值;见下一节。
  • 在离开该函数时,$private:a再次具有其原始的脚本级值。

有关PowerShell

中的变量范围的一般信息
  • 除非使用范围$a明确隐藏变量,否则后代范围可以查看该变量读取值使用变量名而不使用范围限定符(例如private)或需要$a

    • 其他范围默认情况下不会看到私有变量的值,但基本上不能引用它们,即使使用范围修饰符或Get-Variable -Scope进行显式跨范围访问也是如此。 (但是,在相同的范围内,可以使用范围修饰符来引用私有变量,但前提是该范围修饰符有效地针对同一范围,这始终是例如,Get-Variable -Scope为真。
  • 分配给非限定变量,然而,在当前中隐式创建 new 变量({{1范围,它可以影子祖先范围内的同名变量。

    • 也就是说,$local:privateVarNamelocal隐含相同。
  • 要在祖先范围内显式获取/修改变量,请使用$a = 2 ,其中$local:a = 2表示范围级别,Get-Variable / Set-Variable -Scope <n> <name>代表<n>当前范围,0父范围等。
    请注意,默认情况下1会返回Get-Variable个实例,因此,为了只获取,请访问其[System.Management.Automation.PSVariable]属性,或使用.Value } switch,只返回值以开头。

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

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

  • 使用$script:声明变量,可以在任何后代范围中读取并修改,而无需符合条件名称 ;换句话说:只有那个名称的单个变量存在,任何范围都可以使用非限定变量名直接读写。

    • 如果没有单独的Set-Variable -Option AllScope参数,-Scope将应用于当前范围内的变量(例如,脚本范围内的脚本范围)顶级,函数内部函数的局部范围)。因此,为了安全地创建脚本 - 全局变量,您可以访问不受限制的写入,请使用-Option AllScope

    • Set-Variable -Scope Script -Option AllScope-Scope Global 不同:-Option AllScope创建全局可访问变量,读取可能,并修改它 ,需要-Scope Global范围修饰符。另请注意,一个全局变量是 session -global ,因此即使在定义它的脚本终止后它仍然存在。

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

答案 2 :(得分:0)

良好的软件设计意味着最小化耦合(以及其他方面)。在Powershell中,包括使用私有ON每个变量你可以。如果要在某个后续调用的模块中提供值,请明确传递该信息。应该有一个非常好的例外理由不这样做,因为每次你依赖隐含的知识(例如,当你不使用私有变量时在Powershell中发生的那种),你会增加出现意外错误的机会之后(也许几个月后软件中有更多的代码)。