如何使用Powershell修改父范围变量

37

我对PowerShell比较陌生,不太清楚如何修改父级作用域中的变量:

$val = 0
function foo()
{
    $val = 10
}
foo
write "The number is: $val"

运行代码后我得到了:

The number is: 0

我希望它是10。但是PowerShell正在创建一个隐藏父作用域中变量的新变量。

我尝试了这些,但没有成功(根据文档):

$script:$val = 10
$global:$val = 10
$script:$val = 10

但是这些甚至都不能“编译”。

5个回答

43

您不需要使用全局作用域。在shell控制台中已经存在同名变量,您可能会更新它。使用脚本作用域限定符。在使用作用域限定符时,变量名称中不包括$符号。

$script:val=10 

28

使用Set-Variable -Scope 1命令可以直接修改父作用域,无需使用ScriptGlobal作用域。例如:

$val = 0
function foo {
    Set-Variable -scope 1 -Name "Val" -Value "10"
}
foo
write "The number is: $val"

返回:

The number is: 10

有关作用域的更多信息,请参阅Microsoft Docs文章《关于作用域》。该文档的关键摘录如下:

注意:对于使用Scope参数的cmdlet,您还可以按编号引用作用域。编号描述一个作用域相对于另一个作用域的位置关系。作用域0代表当前或本地范围。作用域1表示直接父作用域。作用域2表示父级父作用域,依此类推。如果您已创建许多递归作用域,则编号作用域非常有用。


请注意,递归函数需要相应调整作用域:

$val = ,0
function foo {
    $b = $val.Count
    Set-Variable -Name 'val' -Value ($val + ,$b) -Scope $b
    if ($b -lt 10) {
        foo
    }
}

2
这是最接近问题的答案,因为它精确地访问了直接父级作用域中的变量。 - argonym
这还能用吗?-scope 1是什么意思?虽然在About_Scopes页面中没有记录,但如果您可以指定特定范围的层次结构,那么它似乎很有趣,或者1只是脚本/全局的别名? - Blaisem
它确实有效。我甚至不知道有一个“-Scope 1”的选项...如果有人想知道,它是指父级作用域(0表示当前作用域)。首先使用“Get-Variable -Scope x -Name val”查找您的变量。 - Carl Chang
1
@Blaisem 可能它消失了,但如果是这样,他们已经把它放回去了。我不得不搜索页面内容中的“Scope 1”才找到它,但文档说:“Scope 1表示直接父范围。Scope 2表示父范围的父范围,依此类推。编号范围在创建许多递归范围时非常有用。”我认为这是非常关键的信息,我要将其编辑到答案中。 - NotTheDr01ds

11

让我指出第三种替代方案,尽管答案已经给出。如果你想改变一个变量,不要害怕通过引用传递并以这种方式处理它。

$val=1
function bar ($lcl) 
{
    write "In bar(), `$lcl.Value starts as $($lcl.Value)"
    $lcl.Value += 9
    write "In bar(), `$lcl.Value ends as $($lcl.Value)"
}
$val
bar([REF]$val)
$val

那将返回:

1
In bar(), $lcl.Value starts as 1
In bar(), $lcl.Value ends as 10
10

我知道这已经很老了,但这正是我在寻找的答案。非常感谢! - retryW
最好在函数签名中明确接受引用,使用 function bar ([ref]$lcl),而不仅在函数体中访问 $lcl.Value - dosentmatter

8
如果您想使用这个功能,可以按照以下步骤进行操作:
$global:val=0 
function foo()
{
    $global:val=10 
}
foo
write "The number is: $val"

1
第一行可以写成 $val = 0,因为它在全局范围内是隐式的。 - Joel B Fant
那不起作用。我得到了: 在表达式或语句中出现意外的标记“val”。 - C.J.
3
请确保您拥有$global:val而不是$global:$val。 - Niall Gray

1
也许最简单的方法是点源函数:
$val = 0
function foo()
{
    $val = 10
}
. foo
write "The number is: $val"

在这里的区别在于,你通过“.foo”来调用foo
点号源运行函数与正常运行一样,但它在父范围内运行,因此没有子范围。这基本上消除了作用域。只有当你无意中开始设置或重写变量/定义时,才会出现问题。对于小脚本,这通常不是问题,这使得点号源非常容易使用。
如果你只想要一个单独的变量,那么你可以使用返回值,例如:
$val = 0
function foo()
{
    return 10
}
$val = foo
write "The number is: $val"

(或者在函数中不使用return,因为它不是必需的)

你也可以以这种方式返回多个值以设置多个变量,例如,如果foo返回1,2,3,则可以使用$a,$b,$c = foo

上述方法是处理跨作用域变量的首选方式。

最后一种选择是将write放在函数本身中,这样它就在定义它的同一作用域中写入变量。

当然,您还可以使用作用域命名空间解决方案,通过作用域设置变量Set-Variable,或通过[ref]传递它,如其他人在此处演示的那样。在PowerShell中有许多解决方案。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接