PowerShell中的变量作用域

PowerShell中的变量作用域,powershell,scope,Powershell,Scope,PowerShell令人遗憾的是,函数和脚本块的作用域是动态的 但另一件让我吃惊的事情是,变量在内部范围内的行为就像是写时的拷贝 $array=@("g") function foo() { $array += "h" Write-Host $array } & { $array +="s" Write-Host $array } foo Write-Host $array 输出为: g s g h g 这使得动态范围缩小稍微不那么痛苦。但是如何避

PowerShell令人遗憾的是,函数和脚本块的作用域是动态的

但另一件让我吃惊的事情是,变量在内部范围内的行为就像是写时的拷贝

$array=@("g")
function foo()
{
    $array += "h"
    Write-Host $array
}

& {
    $array +="s"
    Write-Host $array
}
foo

Write-Host $array
输出为:

g s
g h
g
这使得动态范围缩小稍微不那么痛苦。但是如何避免写时复制?

您可以使用范围修饰符或
*-Variable
cmdlet

范围修饰符包括:

  • global
    用于访问/修改最外层范围(例如交互式shell)
  • script
    用于访问/修改正在运行的脚本(
    .ps1
    文件)的范围。如果不运行脚本,则作为
    global
    运行
(有关
*-Variable
cmdlet的
-Scope
参数,请参阅帮助。)

例如,在第二个示例中,要直接修改全局
$array

& {
  $global:array +="s"
  Write-Host $array
}

有关更多详细信息,请参阅帮助主题。

不仅仅是变量。当这表示“项”时,它表示变量、函数、别名和psdrives。所有这些都有范围

LONG DESCRIPTION Windows PowerShell protects access to variables, aliases, functions, and Windows PowerShell drives (PSDrives) by limiting where they can be read and changed. By enforcing a few simple rules for scope, Windows PowerShell helps to ensure that you do not inadvertently change an item that should not be changed. The following are the basic rules of scope: - An item you include in a scope is visible in the scope in which it was created and in any child scope, unless you explicitly make it private. You can place variables, aliases, functions, or Windows PowerShell drives in one or more scopes. - An item that you created within a scope can be changed only in the scope in which it was created, unless you explicitly specify a different scope. 长描述 Windows PowerShell保护对变量、别名、函数和 Windows PowerShell驱动器(PSDrives)通过限制可以读取和读取的位置 改变。通过强制执行一些简单的作用域规则,Windows PowerShell 有助于确保您不会无意中更改应 不能改变。 以下是范围的基本规则: -包含在范围中的项在其所在的范围中可见 已创建并位于任何子作用域中,除非您显式创建它 私有的可以放置变量、别名、函数或窗口 PowerShell驱动器位于一个或多个作用域中。 -在范围内创建的项只能在中更改 除非您明确指定 不同的范围。 您看到的写时拷贝问题是因为Powershell处理阵列的方式。添加到该数组实际上会破坏原始数组并创建一个新数组。由于它是在该作用域中创建的,因此当函数或脚本块退出并且该作用域被释放时,它将被销毁


您可以在更新变量时显式地确定其作用域,也可以使用[ref]对象进行更新,或者编写脚本以更新对象的属性或父作用域中对象或哈希表的哈希表键。这不会在本地作用域中创建新对象,而是修改父作用域中的对象。

PowerShell作用域文章()不错,但太冗长了,因此这是我的引文:

通常,PowerShell作用域与.NET作用域类似。它们是:

  • 全球是公开的
  • 脚本是内部脚本
  • 私有是私有的
  • Local是当前堆栈级别
  • 编号范围从0..N开始,其中每个步骤都达到堆栈级别(0为本地)
下面是一个简单的示例,描述了作用域的用法和效果:

$test = 'Global Scope'
Function Foo {
    $test = 'Function Scope'
    Write-Host $Global:test                                  # Global Scope
    Write-Host $Local:test                                   # Function Scope
    Write-Host $test                                         # Function Scope
    Write-Host (Get-Variable -Name test -ValueOnly -Scope 0) # Function Scope
    Write-Host (Get-Variable -Name test -ValueOnly -Scope 1) # Global Scope
}
Foo

正如您所看到的,您只能在命名范围中使用$Global:test-like语法,$0:test将始终为$null。

而其他文章提供了大量有用的信息,它们似乎只是为了从RTFM中保存您。
没有提到的答案是我觉得最有用的

([ref]$var).value = 'x'
这会修改$var的值,而不管它恰好在哪个范围内。你不需要知道它的范围;只是事实上它已经存在了。要使用OP的示例,请执行以下操作:

$array=@("g")
function foo()
{
    ([ref]$array).Value += "h"
    Write-Host $array
}
& {
    ([ref]$array).Value +="s"
    Write-Host $array
}
foo
Write-Host $array
产生:

g s
g s h
g s h
说明:
([ref]$var)获取指向变量的指针。由于这是一个读取操作,它将解析为实际创建该名称的最新作用域。如果变量不存在,因为[ref]无法创建任何内容,它也会解释错误,因为它只能返回对已经存在的内容的引用

.value然后将您带到保存变量定义的属性;然后你可以设置它

你可能会被诱惑去做这样的事情,因为它有时看起来是有效的

([ref]$var) = "New Value"
不要

它看起来起作用的实例只是一种幻觉,因为PowerShell正在做它只在某些非常狭窄的环境下(例如在命令行上)才会做的事情。你不能指望它。事实上,它在OP示例中不起作用。

谢谢您提供的信息。我读了更多关于scopes的话题。我在本文档中没有看到的一点是,变量的作用域是动态的:(这让我在对脚本进行错误检查时感到困惑;我有一个简单的计数器,每当脚本遇到错误时,计数器都会递增,从而确定电子邮件中发送的信息。无论设置了多少错误条件,最后的错误计数始终为零。现在我明白了原因以及如何修复它。我认为这种方法只在一两种非常有限的情况下回答这个问题。而不是一般的情况。当然函数和别名都是变量。但我并不担心这一点。特别是在某些语言中,函数的作用域与变量的作用域不同(例如common lisp)但是仍然是可变的。如果有一天您编写了一个Powershell脚本,在子作用域中创建别名或函数,这可能会引起一些关注。但您没有回答这个问题。@Richard得到了正确的答案,但这使Powershell成为一种更糟糕的语言。如果作用域语义根据变量的类型而变化,则会导致gn choice,IMO。加上它让动态范围回到了你的面前。:(好吧,它就是这样。我只是试图解释发生了什么。写时拷贝是野兽的本性。明确定义变量的范围并不是避免写时拷贝,它只是指定拷贝将被写入的位置。