PowerShell ForEach / 管道混乱

Posted

技术标签:

【中文标题】PowerShell ForEach / 管道混乱【英文标题】:PowerShell ForEach / Piping confusion 【发布时间】:2011-04-29 21:04:53 【问题描述】:

我正在使用 PowerShell 中的 TFS PowerTools Cmdlet 尝试从我的服务器获取有关变更集和相关工作项的一些信息。我将问题归结为我不理解的行为,我希望它不是特定于 TFS 的(所以那里的人可能能够向我解释这个问题:))

这是我可以开始工作的唯一命令:

Get-TfsItemHistory C:\myDir -recurse -stopafter 5 | % 写主机 $_.WorkItems[0]["Title"]

它符合我的预期——Get-TfsItemHistory 返回一个包含 5 个 ChangeSet 的列表,并将它们通过管道传送到打印出第一个关联 WorkItem 的 Title 的 foreach。那我的问题是什么?我正在尝试编写一个大型脚本,并且我更喜欢编写看起来更像 C# 程序的代码(powershell 语法让我哭了)。每当我尝试以任何其他方式执行上述操作时,WorkItems 集合都是空的。

以下命令(我将其解释为逻辑等效)不起作用(WorkItems 集合为空):

$items = Get-TfsItemHistory C:\myDir -recurse -stopafter 5 $项目 | ForEach-Object 写入主机 $_.WorkItems[0]["Title"]

我更喜欢的那个:

$items = Get-TfsItemHistory C:\myDir -recurse -stopafter 5 foreach($item 中的 $item) $item.WorkItems[0]["标题"] # 做很多其他的事情

我阅读了一篇关于“foreach”运算符和 ForEach-Object Cmdlet 之间区别的文章,但这似乎更像是一场性能辩论。这似乎是关于何时使用管道的问题。

我不确定为什么这三种方法都不起作用。任何见解都值得赞赏。

【问题讨论】:

我不确定问题出在哪里,但它绝对是 TFS Cmdlet 特有的(它们相当可怕,imo)。看来 cmdlet 正在执行延迟加载,一旦管道结束,数据上下文就消失了,加载数据为时已晚,但是 cmdlet 的设计非常复杂,以至于我无法在反射器中跟踪它那么远. 【参考方案1】:

这确实令人困惑。目前的解决方法是像这样抓取这些项目:

$items = @(Get-TfsItemHistory . -r -Stopafter 25 | 
           Foreach $_.WorkItems.Count > $null; $_)

这会访问 WorkItems 集合,这似乎会导致该属性被填充(我知道 - WTF?)。在我想使用 foreach 关键字的情况下,我倾向于使用@() 来生成一个数组。 foreach 关键字的作用是它将迭代一个标量值,包括 $null。因此,如果查询不返回任何内容,$items 将被分配 $null 并且 foreach 将在 $item 设置为 null 的情况下迭代循环一次。现在 PowerShell 通常可以很好地处理空值。但是,如果您将该值交还给 .NET Framework,它通常不会那么宽容。 @() 将保证其中包含 0、1 或 N 个元素的数组。如果为 0,则 foreach 循环根本不会执行其主体。

顺便说一句,你的最后一种方法 - foreach ($item in $items) ... - 应该可以正常工作。

【讨论】:

谢谢,这行得通。我回到了正轨,我没有疯!这确实很奇怪,很高兴知道这是怎么回事,但在这一点上,我已经准备好再写一个小精灵了。

以上是关于PowerShell ForEach / 管道混乱的主要内容,如果未能解决你的问题,请参考以下文章

Powershell foreach

PowerShell 中的嵌套 ForEach()

理解Powershell管道

PowerShell管道和括号——PowerShell从零开始系列之六

Powershell 管道总结

PowerShell初级篇●Powershell管道