在powershell scriptblock中将元素添加到数组会将数组转换为字符串

时间:2018-06-15 09:24:03

标签: arrays powershell scriptblock

我注意到在scriptblocks中使用数组的奇怪行为。以下代码显示了问题:

$array = @("x", "y")

Write-Host "$($array.GetType().Name)"
Write-Host "$($array.GetType().BaseType)"

$bad = {
    $array += "z"
    Write-Host "$($array.GetType().Name)"
    Write-Host "$($array.GetType().BaseType)"
    $array
}

$good = {
    $array = $array.Clone()
    $array += "z"
    Write-Host "$($array.GetType().Name)"
    Write-Host "$($array.GetType().BaseType)"
    $array
}

& $good
& $bad

执行脚本将产生以下输出:

Object[]
array
Object[]
array
x
y
z
String
System.Object
z

脚本块$bad无法正常工作。它将数组转换为字符串,但它应该只是将元素z添加到数组中。如果没有添加元素,则可以按预期使用该数组。

我在PowerShell 5.0和5.1中注意到了这种行为,但在ISE中却没有。这是一个错误还是任何人都可以解释这个?

2 个答案:

答案 0 :(得分:1)

这是一个范围问题。 scriptblocks中赋值操作左侧的变量在本地范围内定义。

本声明

$array = $array.Clone()

克隆全局变量$array的值,并将其分配给 local 变量$array(同名,但由于不同的范围)。然后局部变量$array包含原始数组的副本,因此下一个语句

$array += "z"

向该数组添加一个新元素。

在您的其他脚本块中,您会立即将一个字符串附加到(本地)变量$array。在该上下文中,局部变量为空,因此$array += "z"$array = "z"具有相同的效果,为您留下仅包含字符串“z”的变量。

指定正确的范围,您将获得预期的行为:

$array = @("x", "y")

$not_bad = {
    $script:array += "z"
    Write-Host "$($script:array.GetType().Name)"
    Write-Host "$($script:array.GetType().BaseType)"
    $script:array
}

& $not_bad

但请注意,这实际上会修改全局/脚本范围中的原始数组(您的$good示例保持原始数组不变)。

我不确定我是否会将此行为视为错误,但这绝对是一个问题。

答案 1 :(得分:0)

我想发布我的首选解决方案,该解决方案基于Ansgars解释:

$array = @("x", "y")

$not_bad = {
    $array = $array + "z"
    Write-Host "$($array.GetType().Name)"
    Write-Host "$($array.GetType().BaseType)"
    $array
}

& $not_bad

重要的是在添加更多元素之前分配局部变量(或者更好地创建局部变量)。一个简单的

$array = $array

会这样做,但这一行可能会令人困惑。