Powershell: -OutVariable doesn't unwrap single-object output and creates [System.Collections.ArrayList] values rather than [System.Object[]]

Created on 15 Feb 2017  路  11Comments  路  Source: PowerShell/PowerShell

Note: Also affects other common parameters: see #3773

Steps to reproduce

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

Expected behavior

Type name System.DateTime

Actual behavior

Type name System.Collections.ArrayList

That is, the single-object output stored in the variable targeted with -OutVariable received an _array list_ rather than a scalar (the single-element collection wasn't unwrapped).

Also, it is type [System.Collections.ArrayList] that is always used, in contrast with the [System.Object[]] instances you get with _regular_ assignments.

See also: http://stackoverflow.com/a/40666568/45375

Environment data

PowerShell v6.0.0-alpha (v6.0.0-alpha.15) on Darwin Kernel Version 16.4.0: Thu Dec 22 22:53:21 PST 2016; root:xnu-3789.41.3~3/RELEASE_X86_64
PowerShell v5.1.14393.693 on Microsoft Windows 10 Pro (64-bit; v10.0.14393)
Breaking-Change Committee-Reviewed WG-Engine

Most helpful comment

While I agree that it's weird that this doesn't behave like pipeline output....

The _only_ thing that PowerShell does this collection/single behavior for is pipeline output

These have been collections since the very beginning.

You'll be _potentially_ breaking every single use of these in the history of PowerShell.

Can we follow the process and RFC this?

I'm pretty sure you're going to light up the comments on an RFC for this kind of breaking change.

All 11 comments

IIRC it's been this way since version 1. Changing it now would be a breaking change.

Note that -OutVariable produce live collection, you can reference to it while pipeline is still working:

1..10 | echo -OutVariable a | % { "$a" }

Using not resizable [System.Object[]] instead of [System.Collections.ArrayList] would be bad performance choice.

The behavior is surprising, and possibly falls into the unlikely grey area bucket.

Tagging for committee review.

@PowerShell/powershell-committee reviewed this and agree with the expected result even though it is a breaking change

While I agree that it's weird that this doesn't behave like pipeline output....

The _only_ thing that PowerShell does this collection/single behavior for is pipeline output

These have been collections since the very beginning.

You'll be _potentially_ breaking every single use of these in the history of PowerShell.

Can we follow the process and RFC this?

I'm pretty sure you're going to light up the comments on an RFC for this kind of breaking change.

Please see this RFC proposal for comments on this breaking change.

@Jaykul (Cross-posting this from the related, generalized #3773)

The only thing that PowerShell does this collection/single behavior for is pipeline output

That is not much of a restriction, however, given the pervasive use of pipelines, even in places where it is not obvious:$(...), @(...), &, ., script-block arguments.

To give a few examples, all of which output $True:

$(, 1) -is [int]
(. { , 1 }) -is [int]
(& { , 1 }) -is [int]
('' | Select-Object @{ n='foo'; e = { , 1 } }).foo -is [int]
(, @{ p = , 1 }).p

There is no @(...) example, because @(...) by design ensures _array-valued_ output - though it would still recreate any input collection as a [object[]] array.

@PowerShell/powershell-committee agrees on followig the RFC process specifically the RFC that @rjmholt published

As an afterthought, given that I just stumbled upon it:

Tee-Object -Variable exhibits pipeline, logic, as expected - unlike -OutVariable:

Single-object output produces a _scalar_ (unwraps the output collection):

# Capture output from a command that outputs a *single* object
PS> $null = Get-Item / | Tee-Object -Variable out; $out.GetType().FullName
System.IO.DirectoryInfo  # *scalar* was captured

Multi-object output creates an [object[]] array.

# Capture output from a command that outputs a *multiple* objects
PS> $null = Get-ChildItem / | Tee-Object -Variable out; $out.GetType().FullName
System.Object[]

Yeah, I called out that behavior in the RFC comments 馃槈

In fact, Tee-Object _already_ has the behavior you're looking for: Unlike -OutVariable, Tee-Object doesn't populate it's variable until the end of the pipeline. Because of that, it's able to not output a collection when there's only one item, and it outputs an array when there is (not an ArrayList).

Good point, @Jaykul, thanks - it's been so long...

That I "rediscovered" this inconsistency after all the discussion is telling, however - it baffled me anew.

Was this page helpful?
0 / 5 - 0 ratings