如果不分配给变量,为什么 Powershell Array of Array 显示不同的内容

2024-04-04

如果 cmdlet 返回数组的数组,例如:

function test() {
    $results = New-Object System.Collections.ArrayList
    $array = @()
    for ($idx = 0; $idx -lt 3; $idx++) {
         $obj = New-Object PSObject -Property @{
            "key1" = "value1";
         }
        $array += @($obj)
    }
    [Void] $results.add($array)
    return ,$results.TOArray()
}

那么返回值赋值的时候输出就不同了。

if run test直接显示:

test


Length         : 3
LongLength     : 3
Rank           : 1
SyncRoot       : {@{key1=value1}, @{key1=value1}, @{key1=value1}}
IsReadOnly     : False
IsFixedSize    : True
IsSynchronized : False
Count          : 3

分配给变量时:

$result = test
$result

key1  
----  
value1
value1
value1

如果 cmdlet 返回一级数组,则输出test and $(test)是一样的。

function test() {
    $array = New-Object System.Collections.ArrayList
    for ($idx = 0; $idx -lt 3; $idx++) {
         $obj = New-Object PSObject -Property @{
            "key1" = "value1";
         }
        $array += @($obj)
    }
    return ,$array
} 

test的输出:

key1  
----  
value1
value1
value1

PetSerAl https://stackoverflow.com/users/4003407/petseral在简洁的评论中提供了关键的指针:

The 输出渲染的差异归结为以下事实:

  • 陈述test is a command(对函数、cmdlet 或外部程序的调用)
  • whereas $result(之前捕获了test的输出)是一个表达(仅涉及管道外部的变量引用、PowerShell 运算符和 .NET 方法调用 - 尽管它可能包含nested命令)。

通过输出, $results.ToArray()从你的test函数(函数是命令的一种形式),您正在使用,,数组构造运算符,用于包装$results.ToArray()(这会产生数组的数组)辅助的,暂时的单元素数组,这是确保集合作为对象传递的常用技术单个对象而不是拥有其元素枚举.

那是,辅助。包装数组 is:

  • 总是失去输出到管道,由于管道的自动展开(unrolling)行为,

  • but it 确保wrapped数组被视为单个对象通过管道中的下一个命令.

概念上更清晰,但更详细的等效项, $results.ToArray()你的函数里面是Write-Output -NoEnumerate $results.ToArray();也就是说,PowerShell 通常隐式的输出被显式化,并请求抑制默认行为列举输出集合。

鉴于管道中没有其他命令,tests输出隐式打印到screen。 在当前的情况下,打印数组的数组作为单个对象结果是您看到的属性列表输出格式。

相比之下,$result,由于是一个表达 is 隐式枚举。也就是说,从捕获的数组的数组test- 没有辅助。包装数组! - 已发送一次一个元素到输出格式化系统,然后这些元素呈现得更有意义。


提供一个更简单的例子:

假设你的功能test uses return , , (1..3)输出一个包含 3 元素数组的容器数组,该数组最终包装在 aux 中。单元素数组(顺便说一句:return在PowerShell中只是语法糖exiting函数或脚本块,它与什么没有直接关系output).

执行test那么函数就相当于直接执行以下命令表达:

, , (1..3)

即外部、aux。由于隐式枚举,数组再次被丢弃, (1..3)被渲染为单个对象,得到属性列表格式:

Length         : 3
LongLength     : 3
Rank           : 1
SyncRoot       : {1, 2, 3}
IsReadOnly     : False
IsFixedSize    : True
IsSynchronized : False
Count          : 3

相比之下,执行$result(运行后$result = test) 则相当于:

, (1..3)

即外部、aux。数组在期间丢失$result = test,容器数组现在也被隐式枚举,并且(1..3)作为单个对象渲染更有意义(您无法在视觉上将其与发送1..3直接到管道,即逐个元素):

1
2
3

如何格式化数组以供显示

当命令或表达式既没有在变量中捕获,也没有发送到管道到另一个命令,也没有重定向(使用> or >>),它使用 PowerShell 的默认输出格式系统隐式打印到屏幕(主机)。

您可以想到一个命令,例如:

test

being the equivalent of[1]:

test | Out-Host

Out-Host自动选择一个Format-*用于基于第一个输入对象呈现适合当前输入的 cmdlet: 如果该对象有 4 个或更少的属性,Format-Table被选中;否则,就是Format-List.

但是,如果第一个输入对象是收藏(实现IEnumerable),这是该集合的第一个元素格式化 cmdlet 的选择基于(而不是作为一个整体的集合类型),并且使用该 cmdlet 单独格式化集合的元素。

如果你的$result变量获取输出,输入数组的第一个元素是[pscustomobject]实例(使用创建New-Object PSObject) 拥有 1 个属性,key1;所以,Format-Table选择后,组成数组的 [pscustomobject]` 实例将以表格格式显示。

相比之下,在你的情况下test调用时,输入数组的第一个元素是另一个数组,这就是它本身not进一步列举。Get-Member -InputObject (1,2) -Type Property显示数组有 8 个属性(Count, IsFixedSize, IsReadOnly, IsSynchronized, Length, LongLength, Rank, SyncRoot), 这就是为什么Format-List被选择,将每个属性作为名称/值对在其自己的行上列出。

当然,您可以选择使用格式化 cmdlet明确地,PetSerAl 指出格式化 cmdlet 支持-Expand参数,它使您可以控制作为集合的输入对象的格式:您可以要求枚举集合,即打印它的元素 (-Expand EnumOnly,这是默认值),仅显示集合自己的属性,而不打印其元素(-Expand CoreOnly), 或两者 (-Expand Both).

但请注意,您不能请求额外的枚举级别通过-Expand,所以你的test无法直接格式化输出以显示嵌套数组的各个元素。 然而,通过管道来实现这一点是微不足道的Write-Output,它执行单独渲染元素所需的附加枚举级别:

test | Write-Output

[1] More accurately, as PetSerAl points out, it is: . { test } 2>&1 | Out-Default, enabling users to override the Out-Default cmdlet for custom formatting.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如果不分配给变量,为什么 Powershell Array of Array 显示不同的内容 的相关文章

随机推荐