As far as I understand, issue caused by #2038.
(Write-Output -NoEnumerate 1).GetType().FullName
(1 | Write-Output -NoEnumerate).GetType().FullName
System.Int32
System.Int32
System.Management.Automation.PSObject[]
System.Management.Automation.PSObject[]
> $PSVersionTable
Name Value
---- -----
PSVersion 6.0.0-beta.8
PSEdition Core
GitCommitId v6.0.0-beta.8-46-ge3397b63e756bf432bbe80f5e9c4407d52d6b5b9
OS Microsoft Windows 10.0.15063
Platform Win32NT
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
WSManStackVersion 3.0
Whoops.. meant to cancel my comment, not close the issue. I'm not sure if this is a bug or by design, but I'll label it as a bug until someone says otherwise.
Hi @PetSerAl PetSerAl,
Actually this is by design from all the way back in v1.0. For consistency reasons, the team at that time decided that cmdlets would always return PSObjects. The PowerShell engine invocation APIs also always return collections of PSObject.
@BrucePay As far as I understand PSObject
and PSObject[]
are different things. And that change in behavior of Write-Object
was introduced quite recently. In v1.0 – v5.1 Write-Object
does not behave that way.
@vexx32 Write-Output -NoEnumerate
is not _fully_ fixed in PS6.2.0 and up:
See: https://github.com/PowerShell/PowerShell/issues/5122
PS C:\Users\john.zabroski> ($PSVersionTable).PSVersion
Major Minor Patch PreReleaseLabel BuildLabel
----- ----- ----- --------------- ----------
6 2 1
PS C:\Users\john.zabroski> (Write-Output -NoEnumerate 1).GetType().FullName
System.Collections.Generic.List`1[[System.Object, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]
PS C:\Users\john.zabroski> (1 | Write-Output -NoEnumerate).GetType().FullName
System.Int32
and also (I would argue the following is correct behavior, but just want to document it so that any fix to the above doesn't break this scenario):
PS C:\Users\john.zabroski> (Write-Output -NoEnumerate @()).GetType().FullName
System.Object[]
The problem still exists for:
The problem is fixed for:
🤔 very curious. Okay. Will have to dig into that some...
I just ran into a scenario with this that gives an error on PowerShell 7 preview 5 but works fine on Windows PowerShell.
$object = [pscustomobject]@{Name = 'Test'}
$result = (Write-Output $object -NoEnumerate)
$result.Name = 'New Name'
Because $result
is wrapped, I get this error:
InvalidOperation:
Line |
3 | $result.Name = 'New Name'
| ^ The property 'Name' cannot be found on this object. Verify that the property exists and can be set.
I ran into this trying to get an existing module to work on PowerShell 7. So this is a simplified example of something I found in the wild.
Interesting. Looks like we still have some adjustments to make to the binder. Not sure where those changes would need to be made, but that scenario seems pretty clear we want to have that work. @daxian-dbw any thoughts?
Minimal repros on PSCore 6.2.1:
PS C:\WINDOWS\system32> (Write-Output -NoEnumerate @()).GetType().FullName
System.Object[]
PS C:\WINDOWS\system32> $object = [pscustomobject]@{Name = 'Test'}
PS C:\WINDOWS\system32> (Write-Output $object -NoEnumerate).GetType().FullName
System.Collections.Generic.List`1[[System.Object, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]
Confirmed that is the same on 7-preview.5
.
I _think_ we may be able to fix this scenario by altering the binder to use List[PSObject]
rather than List[object]
-- by using List[object]
here in binding the ValueFromRemainingArguments parameter, it looks like Write-Output is being forced to discard all ETS / instance members on any input objects. That doesn't strike me as a good behaviour at all.
We should probably try to fix this for PS7 if we can, @SteveL-MSFT?
@vexx32 we can still take fixes if the fix itself isn't too risky. Are you going to submit a PR?
I don't know how risky said fix is, or if it's appropriate, and I'm not familiar with the code for the parameter binder itself.
That said, I'm sure I can follow the breadcrumbs and figure it out. I'd prefer if we can get @daxian-dbw's thoughts first, as I think he's pretty familiar with the parameter binder.
@vexx32 we can still take fixes if the fix itself isn't too risky. Are you going to submit a PR?
Changes in the parameter binder is always risky, especially we are nearly at the end of 7.0.0 cycle 😄
That being said, it's definitely worth to better understand the cause of the problem first.
@daxian-dbw are there unit tests for this I can look at?
I might just try taking a stab at fixing it and seeing if my assumption is correct or causes some other problem I'm not expecting. Are you familiar with where this handling for ValueFromRemainingArguments
takes place @daxian-dbw? It might be worth taking a look.
I'd rather not wait till post-ps7 to fix Write-Output
😕 -- doing so likely guarantees folx recommend heavily against using it and/or even using ValueFromRemainingArguments
to avoid these unpredictable behaviours.
As for a _workaround_:
Use -InputObject
explicitly rather than positional parameter binding:
# OK in v6.2+, thanks to -InputObject
(Write-Output -NoEnumerate -InputObject 1).GetType().Name | Should -Be Int32
This is a somewhat ironic reversal from Windows PowerShell, where the inverse workaround (_omitting_ -InputObject
) is required to make -NoEnumerate
behave properly (with input _collections_).
Most helpful comment
Minimal repros on PSCore 6.2.1: