Reputation: 789
I noticed odd behaviour using arrays in scriptblocks. The following code shows the problem:
$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
Executing the script will produce the following output:
Object[]
array
Object[]
array
x
y
z
String
System.Object
z
The scriptblock $bad
does not work as I would expect. It converts the array to string, but it should simply add the element z
to the array. If there is no element added, the array can be used as expected.
I noticed this behaviour in powershell 5.0 and 5.1 but not in the ISE. Is it a bug or can anyone explain this?
Upvotes: 1
Views: 910
Reputation: 789
I would like to post my preferred solution which bases on Ansgars explanation:
$array = @("x", "y")
$not_bad = {
$array = $array + "z"
Write-Host "$($array.GetType().Name)"
Write-Host "$($array.GetType().BaseType)"
$array
}
& $not_bad
Important is the assignment to the local variable (or better to create a local variable) before adding further elements. A simple
$array = $array
would do, but this line may be confusing.
Upvotes: 0
Reputation: 200273
It's a scope issue. The variable on the left side of the assignment operation in the scriptblocks is defined in the local scope.
This statement
$array = $array.Clone()
clones the value of the global variable $array
and assigns it to the local variable $array
(same name, but different variable due to different scope). The local variable $array
then contains a copy of the original array, so the next statement
$array += "z"
appends a new element to that array.
In your other scriptblock you immediately append a string to the (local) variable $array
. In that context the local variable is empty, so $array += "z"
has the same effect as $array = "z"
, leaving you with a variable containing just the string "z".
Specify the correct scope and you'll get the behavior you expect:
$array = @("x", "y")
$not_bad = {
$script:array += "z"
Write-Host "$($script:array.GetType().Name)"
Write-Host "$($script:array.GetType().BaseType)"
$script:array
}
& $not_bad
Beware, however, that this will actually modify the original array in the global/script scope (your $good
example leaves the original array unchanged).
I'm not sure if I would consider this behavior a bug, but it's definitely a gotcha.
Upvotes: 2