19

我已经使用 PowerShell 很多年了,我认为我已经掌握了它的一些更“古怪”的行为,但是我遇到了一个我无法做出头或尾的问题......

我一直使用“return”从函数返回值,但最近我想我应该看看 Write-Output 作为替代方案。但是,PowerShell 是 PowerShell,我发现了一些似乎没有意义的东西(至少对我来说):

function Invoke-X{ write-output @{ "aaa" = "bbb" } };
function Invoke-Y{ return @{ "aaa" = "bbb" } };

$x = Invoke-X;
$y = Invoke-Y;

write-host $x.GetType().FullName
write-host $y.GetType().FullName

write-host ($x -is [hashtable])
write-host ($y -is [hashtable])

write-host ($x -is [pscustomobject])
write-host ($y -is [pscustomobject])

输出:

System.Collections.Hashtable
System.Collections.Hashtable
True
True
True
False

$x 和 $y(或“写入输出”和“返回”)之间有什么区别,这意味着它们都是哈希表,但其中只有一个“-is”是 pscustomobject?除了明显检查变量中的每个哈希表是否也是 pscustomobject 之外,是否有一种通用的方法可以确定与代码的区别?

我的 $PSVersionTable 看起来像这样,以防此行为特定于特定版本的 PowerShell:

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

干杯,

4

2 回答 2

24

return在某种程度上,[pscustomobject]这里是红鲱鱼。

它归结为:

  • 隐式表达式输出与cmdlet产生的输出;using return(没有 cmdlet 调用)属于前一类, usingWrite-Output属于后者。

  • 仅在cmdlet生成的输出中包含输出对象(大部分是不可见的)实例。[psobject]

# Expression output: NO [psobject] wrapper:
@{ "aaa" = "bbb" } -is [psobject] # -> $False

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

请注意 - 令人惊讶的是 -[pscustomobject]与 相同[psobject]:它们都引用 type [System.Management.Automation.PSObject],这是PowerShell 在幕后使用的通常不可见的帮助器类型。
(为了增加混乱,有一个单独的[System.Management.Automation.PSCustomObject]类型。)

在大多数情况下,这个额外的[psobject]包装器是良性的——它的行为主要与被包裹的对象一样——但在某些情况下它会导致细微的不同行为(见下文)。


除了明显检查变量中的每个哈希表是否也是 pscustomobject 之外,是否有一种通用的方法可以确定与代码的区别

请注意,哈希表不是PS 自定义对象 - [psobject]由于[pscustomobject][psobject].

要检测真正的 PS 自定义对象 - 使用 cmdlet 创建[pscustomobject] @{ ... }New-Object PSCustomObject/New-Object PSObject或生成,例如Select-ObjectImport-Csv- 使用:

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

请注意,-as从 Windows PowerShell v5.1 / PowerShell Core v6.1.0 开始,将相关运算符与真正的 PS 自定义对象一起使用已被破坏 - 请参见下文。

作为额外[psobject]包装器是良性的情况的示例,您仍然可以直接测试包装对象的类型:

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

也就是说,尽管有包装器,-is仍然可以识别包装的类型。因此,有点自相矛盾的是,在这种情况下两者都 -is [psobject]返回-is [hashtable]$True即使这些类型是不相关的。


这些差异没有充分的理由,它们给我的印象是抽象的漏洞(实现):内部构造意外地从幕后偷看。

以下 GitHub 问题讨论了这些行为:

于 2018-07-04T17:17:45.490 回答
0

另请注意Write-Output,与 .net 不同,添加调试消息会将返回类型更改为数组。添加写入行可能会破坏函数。

function Invoke-X {
    $o1 = [pscustomobject] @{ foo = 1, 2 }
    return $o1
}

function Invoke-Y {

    $o1 = [pscustomobject] @{ foo = 1, 2 }
    Write-Output "Debug messageY"
    return $o1
 }

function Invoke-Z {
    $o1 = [pscustomobject] @{ foo = 1, 2 }
    Write-Output "Debug messageZ"
    return ,$o1
 }

$x = Invoke-X;
$y = Invoke-Y;
$z = Invoke-Z;

Write-Host
Write-Host "X  Type: " $x.GetType().FullName $x.foo
Write-Host
Write-Host "Y  Type: " $y.GetType().FullName
Write-Host "Y0 Type: " $y[0].GetType().FullName $y[0]
Write-Host "Y1 Type: " $y[1].GetType().FullName $y[1].foo
Write-Host
Write-Host "Z  Type: " $z.GetType().FullName
Write-Host "Z0 Type: " $z[0].GetType().FullName $z[0]
Write-Host "Z1 Type: " $z[1].GetType().FullName $z[1].foo

给出:

X 类型:System.Management.Automation.PSCustomObject 1 2
Y 类型:System.Object[]
Y0 类型:System.String 调试消息Y
Y1 类型:System.Management.Automation.PSCustomObject 1 2
Z 类型:System.Object[]
Z0 类型:System.String 调试信息Z
Z1 类型:System.Management.Automation.PSCustomObject 1 2
于 2021-09-30T16:53:19.210 回答