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

时间:2019-01-12 12:55:45

标签: powershell

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

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()
}

然后,分配返回值后,输出将不同。

如果直接运行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$(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

1 个答案:

答案 0 :(得分:1)

PetSerAl在简短的评论中提供了关键的指针:

输出呈现的差异归结为以下事实:

  • 声明test命令 (对函数,cmdlet或外部程序的调用)
  • $result(以前捕获test的输出)是表达式 (其中仅涉及变量引用,PowerShell的运算符和。 NET方法调用在管道外部-尽管它可能包含 nested 命令)。

通过从, $results.ToArray()函数(函数是命令的一种形式)输出test,您可以使用数组构造运算符,来包装{{1 }}(产生数组的数组)在一个辅助的,暂时的单元素数组中,这是确保集合作为单个对象而不是枚举其元素

辅助。包装器数组是:

    由于管道的自动展开(展开)行为,
  • 在流向管道的输出中始终会丢失

  • ,但它确保通过管道中的下一条命令将包裹的数组视为单个对象 p>

在函数中,$results.ToArray()在概念上更清晰,但更冗长的等效项是, $results.ToArray();也就是说,PowerShell通常隐式的输出是显式的,并请求禁止显示枚举输出集合的默认行为。

鉴于管道中没有其他命令,Write-Output -NoEnumerate $results.ToArray()输出将隐式打印到屏幕。 对于当前情况,将数组数组作为单个对象打印会导致您看到的属性列表输出格式。

相反,由于表达式隐式枚举的 ,因此 tests。也就是说,从$result捕获的数组的数组-没有辅助。包装数组! -一次发送一个元素到输出格式系统,然后这些元素将更有意义地呈现。


要提供一个更简单的示例

假设函数test使用test输出包含3元素数组的容器数组,该数组最终包装在aux中。单元素数组(顺便说一句:PowerShell中的return , , (1..3)只是退出函数或脚本块的语法糖,它与输出)。

执行return函数等效于直接执行以下 expression

test

即外部辅助。由于隐式枚举,数组再次被丢弃,, , (1..3) 被呈现为单个对象,从而导致属性列表格式:

, (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) 期间丢失,容器数组现在也被隐式枚举,并且$result = test作为单个对象呈现得更有意义(您无法从视觉上区别于将其直接发送给(1..3)管道,即逐个元素):

1..3

如何格式化数组以进行显示

当命令或表达式既未捕获到变量中,也未发送到管线中的另一命令,也未重定向(使用1 2 3 >)时,它将隐式打印到屏幕(主机)上,使用PowerShell的默认输出格式系统。

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

>>

等同于 [1]

test

test | Out-Host 根据第一个输入对象自动选择一个Out-Host cmdlet进行渲染,该cmdlet适于手头的输入: 如果该对象具有4个或更少的属性,则选择Format-*;否则为Format-Table

但是,如果第一个输入对象是 collection (实现Format-List),则格式cmdlet的选择基于集合的 first元素 (与整个集合类型相反),然后使用该cmdlet分别设置集合的元素格式。

如果您的IEnumerable变量得到输出,则输入数组的第一个元素是一个$result实例(由[pscustomobject]创建),具有1个属性,New-Object PSObject;因此,选择key1,并以表格格式显示组成数组的[pscustomobject]`实例。

相比之下,在您进行Format-Table调用的情况下,输入数组的第一个元素是另一个数组,其本身进一步被 枚举。 test揭示了一个数组具有8个属性(Get-Member -InputObject (1,2) -Type PropertyCountIsFixedSizeIsReadOnlyIsSynchronizedLength,{ {1}},LongLength),这就是选择Rank的原因,它会将每个属性作为名称/值对单独列出。

当然,您可以选择显式使用格式化cmdlet,PetSerAl指出格式化cmdlet支持SyncRoot参数,该参数使您可以控制输入对象的格式。集合的格式:您可以要求对集合进行枚举,即打印其元素Format-List,这是默认设置),仅显示集合自身的属性,而无需打印其属性元素(-Expand或两者(-Expand EnumOnly)。

但是请注意,您无法通过-Expand CoreOnly请求额外的枚举级别,因此您的-Expand Both输出无法直接格式化为显示嵌套数组的各个元素。 但是,通过管道传递到-Expand来实现这一点很简单,test执行了单独呈现元素所需的附加枚举级别:

Write-Output

[1]更准确地说,正如PetSerAl所指出的,它是:test | Write-Output ,使用户可以覆盖. { test } 2>&1 | Out-Default cmdlet进行自定义格式。