'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 |