'Write-Output -InputObject (,'Test') -NoEnumerate

I think this is a bug that only applies to PowerShell Windows 5.1:

The -NoEnumerate switch doesn't work when the input is provided via the named -InputObject argument:

The following function returns False:

Function Test {Write-Output -InputObject (,'Foo') -NoEnumerate} 
(Test) -is [Array]

While this functiin returns True:

Function Test {Write-Output (,'Foo') -NoEnumerate} 
(Test) -is [Array]

PowerShell Windows

Name                           Value
----                           -----
PSVersion                      5.1.17134.858
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.17134.858
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

The issue doesn't show up in PowerShell Core.
(Both functions return True)

Name                           Value
----                           -----
PSVersion                      6.2.0-preview.1
PSEdition                      Core
GitCommitId                    6.2.0-preview.1
OS                             Microsoft Windows 10.0.17134
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

There is a note although in the description of Write-Output cmdlet regarding the -NoEnumeration switch:

Note

This switch only works correctly with PowerShell Core 6.2 and newer. On older versions of PowerShell Core, the collection is still enumerated even with use of this switch. The behavior in PowerShell Core 6.2 is consistent with Windows PowerShell.

(If I read this correct, I understand that the -NoEnumeration switch should just work for PowerShell Windows)

I have reported the issue here at the Windows PowerShell [UserVoice] as guided here by the PowerShell GitHub Community, but it feels like a black hole... (I have reported issues before but hardly see any response).

Questions:
Is this indeed a bug or am I missing something?
If it is a bug, is the Windows PowerShell [UserVoice] still the correct address to report such an issue?



Solution 1:[1]

Indeed, a bug in Windows PowerShell (up to v5.1, the last version to be released - since the bug is not security-critical, it is unlikely to be fixed) still causes enumeration when -NoEnumerate is passed, albeit just one level.[1]

This bug has been fixed in PowerShell [Core] since (at least) v6.2.3[2]

To demonstrate the problem (all commands below now work as expected in PowerShell [Core]):

WinPS> (Write-Output -NoEnumerate -InputObject 1, 2 | Measure-Object).Count
2 # !! Should be *1*, because a *single array* (with 2 elements) was passed.

Note: It's tempting to omit Measure-Object in the command above, but that would distort the results: Using (), the grouping operator around a command in the context of an expression causes that command's output to be enumerated.

There is a workaround: omit -InputObject, i.e to pass the collection as a positional argument, which, surprisingly, causes the argument to be passed as-is (whereas explicit use of -InputObject enumerates the argument during parameter binding):

# POSITIONAL argument binding, without -InputObject
WinPS> (Write-Output -NoEnumerate 1, 2 | Measure-Object).Count
1 # OK

Note that this workaround doesn't work with pipeline input, although this is an unusual scenario, given that most commands do not output collections as a whole to the pipeline (they send their elements, one by one):

WinPS> ( , (1, 2) | Write-Output -NoEnumerate | Measure-Object ).Count
2 # !! Should be *1*: the unwrapped outer array contains a *single [array]* element

Note: the outer wrapper array (, (...), using the unary form of ,, the array constructor operator) is always necessary if you want to send a collection as a whole through the pipeline using an expression.

As such, this single-element wrapper array technique is a concise and faster alternative to Write-Output -NoEnumerate itself, so, in effect, , (1, 2) by itself is the pipeline-based workaround: it writes the array operand (1, 2) as a whole to the (success) output stream.


[1] Not using -NoEnumerate additionally exhibits a related bug in Windows PowerShell: too much enumeration happens when -InputObject is used explicitly:

One level of recursion is mistakenly applied: An input collection's elements are unexpectedly enumerated as well (again, PowerShell [Core] now works correctly):

WinPS> (Write-Output -InputObject 1, (2, 3) | Measure-Object).Count
3 # !! This should be *2*, because the single input array has only 2 elements.
  # !! That the 2nd element is itself an array should not matter.

Again, the workaround is to omit -InputObject:

# POSITIONAL argument binding, without -InputObject
WinPS> (Write-Output 1, (2, 3) | Measure-Object).Count
2 # OK

[2] However, as of PowerShell (Core) 7.2.2, a bug still exists when you combine
-NoEnumerate with a scalar argument that is positionally bound
:

PSCore> (Write-Output -NoEnumerate 42).GetType().Name
List`1 #`# !! scalar 42 was unexpectedly wrapped in a *list*

The workaround is to use -InputObject explicitly:

# OK, thanks to -InputObject
PSCore> (Write-Output -NoEnumerate -InputObject 42).GetType().Name
Int32 # OK

This bug - which may never get fixed - is being tracked in GitHub issue #5122.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1