PowerShell - “Write-Output” vs “return” in functions

后端 未结 1 345
执笔经年
执笔经年 2021-02-04 04:35

I\'ve been using PowerShell for a number of years, and I thought I had a handle on some of its more \'eccentric\' behaviour, but I\'ve hit an issue I can\'t make head nor tail o

1条回答
  •  闹比i
    闹比i (楼主)
    2021-02-04 05:00

    return and [pscustomobject] are red herrings here, in a way.

    What it comes down to is:

    • Implicit expression output vs. cmdlet-produced output; using return (without a cmdlet call) falls into the former category, using Write-Output into the latter.

    • Output objects getting wrapped in - mostly invisible - [psobject] instances only in cmdlet-produced output.

    # Expression output: NO [psobject] wrapper:
    @{ "aaa" = "bbb" } -is [psobject] # -> $False
    
    # Cmdlet-produced output: [psobject]-wrapped
    (Write-Output @{ "aaa" = "bbb" }) -is [psobject]  # -> $True
    

    Note that - surprisingly - [pscustomobject] is the same as [psobject]: they both refer to type [System.Management.Automation.PSObject], which is the normally invisible helper type that PowerShell uses behind the scenes.
    (To add to the confusion, there is a separate [System.Management.Automation.PSCustomObject] type.)

    For the most part, this extra [psobject] wrapper is benign - it mostly behaves as the wrapped object would directly - but there are instances where it causes subtly different behavior (see below).


    And is there a generalised way I can determine the difference from code, other than obviously checking whether every hashtable I have in a variable is also a pscustomobject

    Note that a hashtable is not a PS custom object - it only appears that way for - any - [psobject]-wrapped object due to [pscustomobject] being the same as [psobject].

    To detect a true PS custom object - created with [pscustomobject] @{ ... } or New-Object PSCustomObject / New-Object PSObject or produced by cmdlets such as Select-Object and Import-Csv - use:

    $obj -is [System.Management.Automation.PSCustomObject] # NOT just [pscustomobject]!
    

    Note that using the related -as operator with a true PS custom object is broken as of Windows PowerShell v5.1 / PowerShell Core v6.1.0 - see below.

    As an example of a situation where the extra [psobject] wrapper is benign, you can still test even a wrapped object for its type directly:

    (Write-Output @{ "aaa" = "bbb" }) -is [hashtable]  # $True
    

    That is, despite the wrapper, -is still recognizes the wrapped type. Therefore, somewhat paradoxically, both -is [psobject] and -is [hashtable] return $True in this case, even though these types are unrelated.


    There is no good reason for these discrepancies and they strike me as leaky abstractions (implementations): internal constructs accidentally peeking from behind the curtain.

    The following GitHub issues discuss these behaviors:

    • Objects are situationally invisibly [psobject]-wrapped, sometimes causing unexpected behavior.

    • Why is [pscustomobject] the same as [psobject], even though a distinct [System.Management.Automation.PSCustomObject] type exists?

    • Should all [psobject] cmdlet parameters be changed to [object]?

    • The -as operator does not recognize System.Management.Automation.PSCustomObject instances as such.

    0 讨论(0)
提交回复
热议问题