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

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

问题描述

考虑以下事项:

function OutputArray{$l = @(,(10,20))$l}(OutputArray) -is [collections.ienumerable]# C:PS>真的(OutputArray).Count# C:PS>2

$l 进入管道时展开".此答案指出 powershell 会展开所有集合.哈希表是一个集合.但是,哈希表当然不受管道的影响:

function OutputHashtable{$h = @{nested=@{prop1=10;prop2=20}}$h}(OutputHashtable) -is [collections.ienumerable]# C:PS>真的(OutputHashtable).Count# C:PS>1

这条评论暗示这就是全部转换为对象数组的 IEnumerable.但是,数组和哈希表都是可枚举的:

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

Powershell 将对象展开"到管道中的具体条件是什么?

解决方案

实证测试结果

我宁愿对这些结果有一个分析基础,但我需要一个答案,这样我才能继续前进.因此,以下是我进行实证测试的结果,以发现哪些集合被 powershell 的管道展开,哪些不是:

列中为真表示可能发生了一些展开.

StartingType ChangedInCmdlet^ ChangedWhenEmitted**------------- -------------------------------系统字符串System.Collections.ArrayList 真 真System.Collections.BitArray True 真System.Collections.HashtableSystem.Collections.Queue True 真System.Collections.SortedListSystem.Collections.Stack True 真System.Collections.Generic.DictionarySystem.Collections.Generic.List True True

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

$result = $starting |Cmdlet

^ ChangedInCmdlet列表示$startingCmdlet中出现时的类型不同.

** ChangedWhenEmitted 列表示 $result 的类型在分配给 $result 时与在 Cmdlet 中发出时不同代码>.

某些类型可能存在一些细微差别.可以通过查看下面测试脚本输出的详细信息来分析这种细微差别.整个测试脚本如下.

测试脚本

[System.Reflection.Assembly]::LoadWithPartialName('System.Collections') |出空[System.Reflection.Assembly]::LoadWithPartialName('System.Collections.Generic') |出空函数 BackThroughPipeline{[CmdletBinding()]param([参数(位置=1)]$InputObject)进程{$InputObject}}函数 EmitTypeName{[CmdletBinding()]param([参数(ValueFromPipeline=$true)]$InputObject)进程{$InputObject.GetType().FullName}}$objects = (新对象字符串'TenTwentyThirty'),([System.Collections.ArrayList]@(10,20,30)),(新对象 System.Collections.BitArray 16),([System.Collections.Hashtable]@{十=10;二十=20;三十=30}),([System.Collections.Queue]@(10,20,30)),([System.Collections.SortedList]@{十=10;二十=20;三十=30}),([System.Collections.Stack]@(10,20,30)),(& {$d = 新对象System.Collections.Generic.Dictionary``2[System.String,int32]"('十',10),('二十',20),('三十',30) |% {$d.Add($_[0],$_[1])}$d}),(& {$l = New-Object System.Collections.Generic.List``1[int32]";10,20,30 |% {$l.Add($_)}$l})$对象 |%{新对象 PSObject -Property @{起始类型 = $_.GetType().FullName起始计数 = $_.Count起始项目 = $_InCmdletType = $_ |发射类型名称InCmdletCount = ($_ | EmitTypeName).CountAfterCmdletType = (BackThroughPipeline $_).GetType().FullNameAfterCmdletItems = (BackThroughPipeline $_)AfterCmdletCount = (BackThroughPipeline $_).CountChangedInCmdlet = if ($_.GetType().FullName -ne ($_ | EmitTypeName) ) {$true};ChangedWhenEmitted = if (($_ | EmitTypeName) -ne (BackThroughPipeline $_).GetType().Fullname ) {$true}}}

Out-Collection Cmdlet

这个测试最终让我创建了一个 cmdlet,它有条件地将集合包装在牺牲数组中,以(希望)可靠地防止循环展开.该 cmdlet 称为 Out-Collection,位于 这个 github 存储库中.>

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天全站免登陆