Wednesday, August 6, 2014

PowerShell Oddities, Take 3

I ran into a weird problem today. I was banging my head against the wall for a little while before I kind of figured out what is going wrong and how to fix it. If you are like me, you would expect the following code to create an array with two strings:

$foo = "Test"
[string[]] @($foo + "_Suffix1", $foo + "_Suffix2")

It even looks like it does. The output is:

Test_Suffix1 Test_Suffix2

But look again. Let’s index into the array:

$foo = "Test"
[string[]] @($foo + "_Suffix1", $foo + "_Suffix2")[0]

As you can see it is both elements concatenated together, not two elements:

Test_Suffix1 Test_Suffix2

I fixed it by change the code to the following:

$foo = "Test"
[string[]] @("$foo`_Suffix1", "$foo`_Suffix2")

Here the output is different, and correct:

Test_Suffix1
Test_Suffix2

Again, if we index into the array:

$foo = "Test"
[string[]] @("$foo`_Suffix1", "$foo`_Suffix2")[0]

Output:

Test_Suffix1

Here is the part that really threw me off:

$foo = "Test" 
([string[]] @($foo + "_Suffix1", $foo + "_Suffix2")).GetType() 
([string[]] @("$foo`_Suffix1", "$foo`_Suffix2")).GetType()
IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String[]                                 System.Array

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String[]                                 System.Array

You are getting your array.

That made it hard to figure out because the code was actually buried deep in a sequence of calls. In the former, you get an array with everything stuffed into the first element. In the latter, you actually get two elements.

What is really interesting is that given the above, you would think the following would be the same as the code that worked, but in fact it acts like the failing code:

$foo = "Test" 
[string[]] @($foo + "`_Suffix1", $foo + "`_Suffix2")[0]

Output:

Test_Suffix1 Test_Suffix2

Clearly what I think is going on, is not. Let’s dig in a little further…

See, I was assuming that the _ was throwing the concatenation off somehow. Which stands to reason when you try something like this:

$foo = "Test" 
[string[]] @("$foo_Suffix1", "$foo_Suffix2")

In which case, the string parser sees those as all one variable.

Let’s try the original with some extra braces:

$foo = "Test"
[string[]] @(($foo + "_Suffix1"), ($foo + "_Suffix2"))

Output:

Test_Suffix1
Test_Suffix2

A little more experimentation:

PS C:\> [string[]] @("asfsadf", "asfdsdf")
asfsadf
asfdsdf
PS C:\> [string[]] @("asdf" + "asfsadf", "asfdsdf")
asdfasfsadf asfdsdf

So, PowerShell concatenation changes the meaning of the comma. This is going to require further investigation. My hunch is that the + takes precedence over the , and what you are actually doing is passing an array to the concatenation operator.

In other words, what I think is going on is:

[string[]] @(("asdf" + "asfsadf"), "asfdsdf")

But what is actually going on:

[string[]] @("asdf" + ("asfsadf", "asfdsdf"))

Definitely something to be aware of.

1 comment: