1

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?

2 Answers 2

2

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks. While your answer is right I realize that the script example may not archive the whole problem. The original script has different behaviour in console and ISE. The example script behaves equal.
ISE and console are different host processes that are known to behave differently in some respects. I'd suggest posting a new question with a updated code and problem description if that issue persists.
0

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.

4 Comments

It is hard to believe that the different behaviour of $a = $a + "z" and $a += "z" was intention and not a bug.
$a += "z" isn't just syntactic sugar for $a = $a + "z". It's semantic is actually slightly different The latter statement means "append 'z' to the variable on the right-hand side and assign the result to the variable on the left-hand side" whereas the former means "append 'z' to the variable on the left-hand side". They produce the same result when you're using the same variable on both sides of the assignment. However, in your particular scenario that is not the case ($a on the left-hand side is in the local scope, $a on the right-hand side is in the global scope).
You are totally right. But it makes code hard to understand (and to write). In c++ one can code different behaviour for both operators + and += but one should not, cause it is confusing. In PS it is by design!?
The problem is not with the operators per se, but with the different evaluation of scopes on either side of the assignment. Like I said, it's definitely a gotcha. I don't know the reasons for the decision to make PowerShell (or the underlying .Net framework) evaluate things that way, though. Maybe @BrucePayette could shed some light.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.