什么决定Powershell管道是否将展开集合? [英] What determines whether the Powershell pipeline will unroll a collection?

查看:217
本文介绍了什么决定Powershell管道是否将展开集合?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

# array
C:\> (1,2,3).count
3
C:\> (1,2,3 | measure).count
3

# hashtable
C:\> @{1=1; 2=2; 3=3}.count
3
C:\> (@{1=1; 2=2; 3=3} | measure).count
1

# array returned from function
C:\> function UnrollMe { $args }
C:\> (UnrollMe a,b,c).count
3
C:\> (UnrollMe a,b,c | measure).count
1
C:\> (1,2,3).gettype() -eq (UnrollMe a,b,c).gettype()
True

与HashTables的差异是相当了解 ,尽管官方文档仅倾斜提及(通过示例)。

The discrepancy with HashTables is fairly well known, although the official documentation only mentions it obliquely (via example).

但是,函数的问题是我的新闻。我很震惊,它以前没有咬我。有没有一些指导原则,我们脚踏车可以遵循?我知道在C#中编写cmdlet时,有一个 WriteObject的重载,你可以明确地控制枚举,但AFAIK没有这样的结构在Posh语言本身。正如最后的例子所示,Posh解释器似乎相信被管道的对象的类型没有区别。我怀疑可能有一些对象vs PSObject怪异在底层,但是这是没有什么用,当你写纯粹的Posh,期望脚本语言只是工作。

The issue with functions, though, is news to me. I'm kind of shocked it hasn't bitten me before now. Is there some guiding principle we scripters can follow? I know that when writing cmdlets in C# there's an overload of WriteObject where you can control enumeration explicitly, but AFAIK there's no such construct in the Posh language itself. As the final example shows, the Posh interpreter seems to believe there is no difference in the type of objects being piped. I suspect there may be some Object vs PSObject weirdness under the hood, but that's of little use when you're writing pure Posh and expect the script language to "just work."

/ EDIT /

Keith是正确的,指出在我的例子中,我传入一个string []参数而不是3个字符串参数。换句话说,Measure-Object说Count = 1的原因是因为它看到一个数组数组,其第一个元素是@(a,b,c)。很公平。这种知识允许你以几种方式解决这个问题:

Keith is correct to point out that in my example, I'm passing in a single string[] argument rather than 3 string arguments. In other words, the reason Measure-Object says Count=1 is because it's seeing a single array-of-arrays whose first element is @("a", "b", "c"). Fair enough. This knowledge allows you to work around the issue in several ways:

# stick to single objects
C:\> (UnrollMe a b c | measure).count
3

# rewrite the function to handle nesting
C:\> function UnrollMe2 { $args[0] }
C:\> (UnrollMe2 a,b,c | measure).count
3

# ditto
C:\> function UnrollMe3 { $args | %{ $_ } }
C:\> (UnrollMe3 a,b,c | measure).count
3

't解释一切...

However, it doesn't explain everything...

# as seen earlier - if we're truly returning @( @("a","b","c") ) why not count=1?
C:\> (UnrollMe a,b,c).count
3

# our theory must also explain these results:
C:\> ((UnrollMe a,b,c) | measure).count
3
C:\> ( @(@("a","b","c")) | measure).count
3
C:\> ((UnrollMe a,b,c d) | measure).count
2

我可以推断还有另一个规则:如果你有一个数组只有一个元素,解析器在表达式模式,那么解释器将解开所述元素。我缺少更多的细节?

From what I can extrapolate there's another rule in play: if you have an array with exactly one element AND the parser is in expression mode, then the interpreter will "unwrap" said element. Any more subtleties I'm missing?

推荐答案

$ args已展开。请记住,函数参数通常使用空格传递以分隔它们。当你传入1,2,3时,你传入一个参数,它是一个三个数字的数组,分配给$ args [0]:

$args is unrolled. Remember that function parameters are normally passed using space to separate them. When you pass in 1,2,3 you are passing in a single argument that is an array of three numbers that gets assigned to $args[0]:

PS> function UnrollMe { $args }
PS> UnrollMe 1 2 3 | measure

Count    : 3

将结果分组表达式(或子表达式例如 $())使其再次有资格展开,因此以下展开包含由UnrollMe返回的1,2,3的对象[]:

Putting the results (an array) within a grouping expression (or subexpression e.g. $()) makes it eligible again for unrolling so the following unrolls the object[] containing 1,2,3 returned by UnrollMe:

PS> ((UnrollMe 1,2,3) | measure).Count
3

等于:

PS> ((1,2,3) | measure).Count
3

PS> ((1,2),3) | %{$_.GetType().Name}
Object[]
Int32

在已经是数组的数组上使用数组子表达式( @())无论应用它多少次都没有效果。 :-)如果要防止展开,请使用逗号运算符,因为它将始终创建另一个外部数组,该数组将被展开。注意,在这种情况下,你不会真正阻止展开,你只是通过引入一个外部包装器数组,展开而不是原始数组,例如:工作围绕展开:

Using an array subexpression (@()) on something that is already an array has no effect no matter how many times you apply it. :-) If you want to prevent unrolling use the comma operator because it will always create another outer array which gets unrolled. Note that in this scenario you don't really prevent unrolling, you just work around the unrolling by introducing an outer "wrapper" array that gets unrolled instead of your original array e.g.:

PS> (,(1,2,3) | measure).Count
1

当你执行这个:

PS> (UnrollMe a,b,c d) | %{$_.GetType().Name}
Object[]
String

您可以看到UnrollMe返回两个项目(a,b,c)作为数组,d作为标量。这两个项目被分别发送到管道,这是最终的计数是2。

You can see that UnrollMe returns two items (a,b,c) as an array and d as a scalar. Those two items get sent down the pipeline separately which is the resulting count is 2.

这篇关于什么决定Powershell管道是否将展开集合?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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