8

I'm sure there is a valid reason why this happens but I don't know what it is. I have the following code

$Deleted = $Items[0].ParentNode.RemoveChild($Items[0])
Write-Output $Deleted

If I call this code using

Do-Something -OutVariable Test

I get the output on the screen and in the variable as expected, but the variable is an ArraryList with a single element that is of type XmlElement

If Instead I run

$Test = Do-Something

I don't get the output on the screen, as expected, and the returned output is a single object of type XmlElement

Why does OutVariable return an ArrayList rather than return the single item the function returns?

Thanks

Martin
  • 503
  • 1
  • 6
  • 20

1 Answers1

10

As of PowerShell (Core) 7.3.2:

  • PowerShell unexpectedly does not unwrap a command's single-object output with
    -OutVariable
    : That is, even if a command outputs only one object, that object is unexpectedly stored as an array (array list) in the target variable whose name is passed to the common parameter -OutVariable parameter (or its alias, -ov).

  • Additionally, the type of the value stored in the target variable is always [System.Collections.ArrayList], an array list, which is inconsistent with capturing multi-object output in a variable by regular assignment, which results in System.Object[], a regular array.

See GitHub issue #3154.

Here's a concise test that demonstrates the problem:

$null = Get-Date -OutVariable ov; $ov.GetType().FullName

As of the versions listed above, this outputs:

System.Collections.ArrayList

even though the output is (conceptually) only a single [datetime] ([System.DateTime]) instance.

Contrast this with a regular assignment: $v = Get-Date; $v.GetType().FullName yields System.DateTime.


The workaround is to wrap the output-variable reference in the subexpression operator ($(...)), which gives you the same output as via the regular output (success) stream:

  • a single-element collection is unwrapped (only outputs the element itself)
  • a multi-element collection is returned as a System.Object[] array.
# Note the $(...) around $ov
PS> $null = Get-Date -OutVariable ov; $($ov).GetType().FullName
System.DateTime  # 1-element System.Collections.ArrayList unwrapped

Of course, you could also use $ov[0] if you only ever expect a single output object.

A demonstration that the workaround is effective with multi-element output too:

PS> $null = Get-ChildItem / -OutVariable ov; $($ov).GetType().FullName
System.Object[] # multi-element System.Collections.ArrayList converted to array
mklement0
  • 382,024
  • 64
  • 607
  • 775
  • i think this got fixed in 5.1.14393.2969 https://stackoverflow.com/questions/56799474/how-to-redirect-output-from-command-to-variable – Cataster Jun 27 '19 at 23:50
  • @Cataster: No, it hasn't: just try `$null = Get-Date -OutVariable ov; $ov.GetType().FullName`, and you'll see that even single-object output is still stored in a `[System.Collections.ArrayList]` instance. – mklement0 Jun 28 '19 at 01:48