简体   繁体   English

powershell在什么条件下展开管道中的项目?

[英]In what conditions does powershell unroll items in the pipeline?

Consider the following: 考虑以下:

function OutputArray{
    $l = @(,(10,20))
    $l
}

(OutputArray) -is [collections.ienumerable]
# C:\ PS> True
(OutputArray).Count
# C:\ PS> 2

$l is "unrolled" when it enters the pipeline . $l 进入管道时“展开” This answer states that powershell unrolls all collections . 这个答案表明powershell展开了所有集合 A hashtable is a collection . 哈希表是一个集合 However, a hashtable is of course unaffected by the pipeline: 但是,哈希表当然不受管道的影响:

function OutputHashtable{
    $h = @{nested=@{prop1=10;prop2=20}}
    $h
}

(OutputHashtable) -is [collections.ienumerable]
# C:\ PS> True
(OutputHashtable).Count
# C:\ PS> 1

This comment suggests that it is all IEnumerable that are converted to object arrays . 这条评论表明,所有IEnumerable都转换为对象数组 However, both array and hashtable are ienumerable: 但是,数组和散列表都是不可数的:

@(,(10,20)) -is [collections.ienumerable]
#True
@{nested=@{prop1=10;prop2=20}} -is [collections.ienumerable]
#True

What, exactly, are the conditions where powershell "unrolls" objects into the pipeline? 究竟,PowerShell将对象“展开”到管道中的条件是什么?

Empirical Test Results 实证检验结果

I'd rather have an analytical basis for these results, but I need an answer so I can move on. 我宁愿对这些结果有一个分析基础,但我需要一个答案,所以我可以继续前进。 So, here are the results of my stab at an empirical test to discover which collections are unrolled by powershell's pipeline, and which aren't: 因此,以下是我在实证测试中的结果,以发现哪些集合由powershell的管道展开,哪些不是:

True in a column indicates there's probably some unrolling occurring. 列中的True表示可能会发生一些展开。

StartingType                          ChangedInCmdlet^  ChangedWhenEmitted**
------------                          ---------------   ------------------
System.String                                           
System.Collections.ArrayList          True              True
System.Collections.BitArray           True              True
System.Collections.Hashtable
System.Collections.Queue              True              True
System.Collections.SortedList
System.Collections.Stack              True              True
System.Collections.Generic.Dictionary                   
System.Collections.Generic.List       True              True

These are results for a line of powershell that looks like this: 这些是PowerShell的结果,如下所示:

$result = $starting | Cmdlet

^ The ChangedInCmdlet column indicates that the type of $starting is different when it appears inside Cmdlet . ^ ChangedInCmdlet列表示当它出现在Cmdlet时, $starting的类型是不同的。

** The ChangedWhenEmitted column indicates that the type of $result is different when it is assigned to $result from when it was emitted inside Cmdlet . ** ChangedWhenEmitted列指示$result的类型在从Cmdlet内部发出时分配给$ result时是不同的。

There's probably some nuance in there for some types. 对于某些类型,可能存在一些细微差别。 That nuance can be analyzed by looking at the details of the output of the test script below. 可以通过查看下面测试脚本输出的详细信息来分析这种细微差别。 The whole test script is below. 整个测试脚本如下。

Test Script 测试脚本

[System.Reflection.Assembly]::LoadWithPartialName('System.Collections') | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName('System.Collections.Generic') | Out-Null

Function BackThroughPipeline{
    [CmdletBinding()]
    param([parameter(position=1)]$InputObject)
    process{$InputObject}
}

Function EmitTypeName{
    [CmdletBinding()]
    param([parameter(ValueFromPipeline=$true)]$InputObject)
    process{$InputObject.GetType().FullName}
}

$objects = (New-Object string 'TenTwentyThirty'),
           ([System.Collections.ArrayList]@(10,20,30)),
           (New-Object System.Collections.BitArray 16),
           ([System.Collections.Hashtable]@{ten=10;twenty=20;thirty=30}),
           ([System.Collections.Queue]@(10,20,30)),
           ([System.Collections.SortedList]@{ten=10;twenty=20;thirty=30}),
           ([System.Collections.Stack]@(10,20,30)),
           (& {
               $d = New-Object "System.Collections.Generic.Dictionary``2[System.String,int32]"
               ('ten',10),('twenty',20),('thirty',30) | % {$d.Add($_[0],$_[1])}
               $d
           }),
           (& {
               $l = New-Object "System.Collections.Generic.List``1[int32]"
               10,20,30 | % {$l.Add($_)}
               $l
           })

$objects | 
    % {
        New-Object PSObject -Property @{
                StartingType  = $_.GetType().FullName
                StartingCount = $_.Count
                StartingItems = $_
                InCmdletType  = $_ | EmitTypeName
                InCmdletCount = ($_ | EmitTypeName).Count
                AfterCmdletType   = (BackThroughPipeline $_).GetType().FullName
                AfterCmdletItems  = (BackThroughPipeline $_)
                AfterCmdletCount  = (BackThroughPipeline $_).Count
                ChangedInCmdlet    = if ($_.GetType().FullName -ne ($_ | EmitTypeName) ) {$true};
                ChangedWhenEmitted = if (($_ | EmitTypeName) -ne (BackThroughPipeline $_).GetType().Fullname ) {$true}
            }
    }

Out-Collection Cmdlet Out-Collection Cmdlet

This testing eventually led me to create a cmdlet that conditionally wraps collections in sacrificial arrays to (hopefully) reliably prevent loop unrolling. 这个测试最终促使我创建了一个cmdlet,它有条件地将牺牲数组中的集合包装起来(希望)可靠地防止循环展开。 That cmdlet is called Out-Collection and is in this github repository . 该cmdlet称为Out-Collection ,位于此github存储库中

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM