PowerShell令人遗憾的是功能和脚本块是动态范围的。
但令我感到惊讶的另一件事是变量在内部范围内表现为写时复制。
$array=@("g")
function foo()
{
$array += "h"
Write-Host $array
}
& {
$array +="s"
Write-Host $array
}
foo
Write-Host $array
输出结果为:
g s
g h
g
这使动态范围变得不那么痛苦。但是我该如何避免写时复制?
答案 0 :(得分:82)
PowerShell范围文章(about_Scopes)很好,但是太冗长了,所以这是我article的引用:
通常,PowerShell范围类似于.NET范围。他们是:
这是一个简单的例子,它描述了范围的用法和效果:
$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。
答案 1 :(得分:67)
您可以使用范围修饰符或*-Variable
cmdlet。
范围修饰符为:
global
用于在最外层范围内访问/修改(例如,交互式shell)script
用于访问/修改运行脚本(.ps1
文件)的范围。如果没有运行脚本,则操作为global
。(有关-Scope
cmdlet的*-Variable
参数,请参阅帮助。)
EG。在第二个示例中,要直接修改全局$array
:
& {
$global:array +="s"
Write-Host $array
}
有关详细信息,请参阅帮助主题 about_scopes 。
答案 2 :(得分:15)
不只是变数。当这表示“item”时,它表示变量,函数,别名和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.
您看到的写入问题副本是因为Powershell处理数组的方式。添加到该数组实际上会破坏原始数组并创建一个新数组。由于它是在该范围内创建的,因此在函数或脚本块退出并且范围被处理时会被销毁。
您可以在更新变量时显式定义变量,或者可以使用[ref]对象进行更新,或者编写脚本以便更新对象的属性或对象的散列表键或父范围中的哈希表。这不会在本地作用域中创建新对象,它会修改父作用域中的对象。
答案 3 :(得分:1)
虽然其他帖子提供了许多有用的信息,但它们似乎只是使您脱离了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示例中不起作用。