簡體   English   中英

是什么決定了Powershell管道是否會展開一個集合?

[英]What determines whether the Powershell pipeline will unroll a collection?

# 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的差異是眾所周知的 ,盡管官方文檔僅提到它(通過示例)。

但是,功能問題對我來說是個新聞。 我有點震驚,現在還沒有咬過我。 我們編程人員可以遵循一些指導原則嗎? 我知道在C#中編寫cmdlet時會出現WriteObject重載 ,您可以在其中明確地控制枚舉,但是AFAIK在Posh語言本身中沒有這樣的構造。 正如最后一個例子所示,Posh解釋器似乎相信被管道對象的類型沒有區別。 我懷疑在引擎蓋下可能會有一些Object vs PSObject的怪異,但是當你編寫純粹的Posh並期望腳本語言“正常工作”時,這沒什么用處。

/編輯/

基思是正確的指出,在我的例子中,我傳入一個字符串[]參數而不是3個字符串參數。 換句話說,Measure-Object說Count = 1的原因是因為它看到的是一個數組,其第一個元素是@(“a”,“b”,“c”)。 很公平。 這些知識允許您以多種方式解決問題:

# 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

但是,它並不能解釋一切......

# 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

根據我可以推斷的另一個規則:如果你有一個只有一個元素的數組並且解析器處於表達式模式 ,那么解釋器將“解包”所述元素。 我錯過了更多的微妙之處?

$ args已展開。 請記住,函數參數通常使用空格來分隔它們。 當您傳入1,2,3時,您傳入的是一個由三個數組組成的數組,該數組被賦值給$ args [0]:

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

Count    : 3

將結果(數組)放在分組表達式(或子表達式,例如$() )中使其再次符合展開條件,以便下面展開包含UnrollMe返回的1,2,3的對象[]:

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

這相當於:

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

順便說一句,它不僅適用於具有一個元素的數組。

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

對已經是數組的東西使用數組子表達式( @() )無論你應用它多少次都沒有效果。 :-)如果你想阻止展開使用逗號運算符,因為它將始終創建另一個展開的外部數組。 請注意,在這種情況下,您並沒有真正阻止展開,您只需通過引入一個外部“包裝”數組來展開展開,而不是原始數組,例如:

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

最后,執行此操作時:

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

您可以看到UnrollMe將兩個項目(a,b,c)作為數組返回,d作為標量返回。 這兩個項目分別從管道發送,結果計數為2。

它似乎與Measure-Object如何工作以及如何沿管道傳遞對象有關。

當你說

1,2,3 | measure

你得到3個Int32對象傳遞給管道,測量對象然后計算它在管道上看到的每個對象。

當您使用函數“展開它”時,您將獲得一個傳遞到管道上的單個數組對象,該對象將對象計數為1,它不會嘗試迭代數組中的對象,如下所示:

PS C:\> (measure -input 1,2,3).count
1

可能的解決方法是使用foreach將數組“重新滾動”到管道上:

PS C:\> (UnrollMe 1,2,3 | %{$_} | measure).count
3

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM