Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using OutVariable Creates ArrayList

Tags:

powershell

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

like image 815
Martin Avatar asked Nov 17 '16 22:11

Martin


1 Answers

As of PowerShell [Core] 7.0:

  • 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 this GitHub issue.

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
like image 62
mklement0 Avatar answered Oct 17 '22 17:10

mklement0