PowerShell -函数中的“Write-Output”vs“return”

nue99wik  于 2023-06-23  发布在  Shell
关注(0)|答案(2)|浏览(143)

我已经使用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(或者'write-output'和'return')之间的区别是什么,这意味着它们都是哈希表,但其中只有一个'-是'一个pscustomobject?除了明显检查变量中的每个哈希表是否也是一个pscustom对象之外,有没有一种通用的方法可以确定代码的差异?
我的$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

干杯
M型

nfs0ujit

nfs0ujit1#

return[pscustomobject]在某种程度上是转移注意力的东西。
归结起来就是:

  • 隐式 * 表达式 * 输出与 cmdlet-生成的输出;使用return(不使用cmdlet调用)福尔斯前一种,使用Write-Output属于后一种。
  • 输出对象得到 * Package *-大部分是不可见的-[psobject]示例仅在 cmdlet 中-产生的输出。
# Expression output: NO [psobject] wrapper:
@{ "aaa" = "bbb" } -is [psobject] # -> $False

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

请注意,令人惊讶的是,[pscustomobject][psobject]相同:它们都引用类型[System.Management.Automation.PSObject],这是PowerShell在幕后使用的 * 通常不可见的助手类型 *。
(To更令人困惑的是,还有一个单独的[System.Management.Automation.PSCustomObject]类型。)
在大多数情况下,这个额外的[psobject] Package 器是良性的--它的行为与被 Package 对象的行为基本相同--但在某些情况下,它会导致微妙的不同行为(见下文)。
除了检查变量中的每个哈希表是否也是一个pscustomobject之外,有没有一种通用的方法可以确定代码中的差异
请注意,哈希表 * 不是 * PS自定义对象-它只出现在- any -[psobject]-wrapped对象中,因为[pscustomobject][psobject]相同。
要检测真正的PS自定义对象(使用[pscustomobject] @{ ... }New-Object PSCustomObject/New-Object PSObject创建或由Select-ObjectImport-Csv等cmdlet生成),请用途:

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

请注意,从Windows PowerShell v5.1 / PowerShell Core v6.1.0开始,将相关的-as运算符与真正的PS自定义对象一起使用将被破坏-请参见下文。
作为一个额外的[psobject] Package 器是良性的情况的例子,您仍然可以直接测试 Package 的对象的类型:

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

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

  • 这些差异没有很好的理由 *,它们让我觉得是泄漏的抽象(实现):内部结构意外地从幕后窥视。

以下GitHub issues讨论了这些行为:

polhcujo

polhcujo2#

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

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  Type:  System.Management.Automation.PSCustomObject 1 2
Y  Type:  System.Object[]
Y0 Type:  System.String Debug messageY
Y1 Type:  System.Management.Automation.PSCustomObject 1 2
Z  Type:  System.Object[]
Z0 Type:  System.String Debug messageZ
Z1 Type:  System.Management.Automation.PSCustomObject 1 2

相关问题