在什么情况下,powershell展开项目在管道? [英] In what conditions does powershell unroll items in the pipeline?
问题描述
请考虑以下方面:
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]
pre>
#True
@ {nested = @ {prop1 = 10; prop2 = 20}} -is [collections.ienumerable]
#True
究竟是什么,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 insideCmdlet
.** The
ChangedWhenEmitted
column indicates that the type of$result
is different when it is assigned to $result from when it was emitted insideCmdlet
.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
CmdletThis 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屋!