在什么情况下,powershell展开项目在管道? [英] In what conditions does powershell unroll items in the pipeline?

查看:110
本文介绍了在什么情况下,powershell展开项目在管道?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请考虑以下方面:

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

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

$ l 当它进入管道时展开此回答说明powershell会展开所有集合散列表是一个集合。但是,散列表当然不受管道的影响:

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

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

这个注释建议它是所有IEnumerable,转换为对象数组。但是,数组和哈希表都是ienumerable:

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

究竟是什么,powershell将对象展开到管道中?

解决方案

经验测试结果



我希望有这些结果的分析基础,但我需要一个答案,所以,下面是我的经验测试的结果,以发现哪些集合是由powershell的管道展开,而不是:



  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

这些是一行powershell的结果,如下所示:

  $ result = $ starting | Cmdlet 

^ ChangedInCmdlet Cmdlet 中出现 $ starting 的类型不同。



** ChangedWhenEmitted 列表示 $ result 是不同的,当它分配给$ result从 Cmdlet 时发出。



一些细微差别在那里有些类型。该细微差别可以通过查看下面的测试脚本输出的细节来分析。整个测试脚本如下。



测试脚本



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

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

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

$ objects =(New-Object string'TenTwentyThirty'),
([System.Collections.ArrayList] @ ,30)),
(New-Object System.Collections.BitArray 16),
([System.Collections.Hashtable] @ {ten = 10; twenty = 20; thirty = b $ b([System.Collections.Queue] @(10,20,30)),
([System.Collections.SortedList] @ {ten = 10; twenty = 20; thirty = 30}),
([System.Collections.Stack] @(10,20,30)),
(& {
$ d = New-ObjectSystem.Collections.Generic.Dictionary ... [System.String,int32]
('ten',10),('twenty',20),('thirty',30)|%{$ d.Add($ _ [0],$ _ [1])}
$ d
}),
(& {
$ l = New-ObjectSystem.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



这个测试最终导致我创建一个cmdlet,它有条件地将集合包裹在牺牲数组中(希望)可靠地防止循环展开。该cmdlet名为 Out-Collection ,位于此github存储库中。 / p>

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. This answer states that powershell unrolls all collections. 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. 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?

解决方案

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:

True in a column indicates there's probably some unrolling occurring.

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:

$result = $starting | Cmdlet

^ The ChangedInCmdlet column indicates that the type of $starting is different when it appears inside Cmdlet.

** The ChangedWhenEmitted column indicates that the type of $result is different when it is assigned to $result from when it was emitted inside Cmdlet.

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

This testing eventually led me to create a cmdlet that conditionally wraps collections in sacrificial arrays to (hopefully) reliably prevent loop unrolling. That cmdlet is called Out-Collection and is in this github repository.

这篇关于在什么情况下,powershell展开项目在管道?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆