简体   繁体   English

PowerShell ForEach / Piping混乱

[英]PowerShell ForEach / Piping confusion

I am using the TFS PowerTools Cmdlets in PowerShell to try to get at some information about Changesets and related WorkItems from my server. 我在PowerShell中使用TFS PowerTools Cmdlet尝试从我的服务器获取有关Changeset和相关WorkItem的一些信息。 I have boiled the problem down to behavior I don't understand and I am hoping it is not TFS specific (so someone out there might be able to explain the problem to me :) ) 我把问题归结为我不理解的行为,我希望它不是TFS特定的(所以有人可能能够向我解释这个问题:))

Here's the only command that I can get to work: 这是我可以开始工作的唯一命令:

Get-TfsItemHistory C:\myDir -recurse -stopafter 5 | % { Write-Host $_.WorkItems[0]["Title"] }

It does what I expect - Get-TfsItemHistory returns a list of 5 ChangeSets, and it pipes those to a foreach that prints out the Title of the first associated WorkItem. 它做了我所期望的 - Get-TfsItemHistory返回一个包含5个ChangeSet的列表,并将它们传递给foreach,它打印出第一个关联WorkItem的Title。 So what's my problem? 那我的问题是什么? I am trying to write a large script, and I prefer to code things to look more like a C# program (powershell syntax makes me cry). 我正在尝试编写一个大型脚本,我更喜欢将事物编写为更像C#程序(powershell语法让我哭泣)。 Whenever I try to do the above written any other way, the WorkItems collection is null. 每当我尝试以任何其他方式执行上述操作时,WorkItems集合为空。

The following commands (which I interpret to be logically equivalent) do not work (The WorkItems collection is null): 以下命令(我解释为在逻辑上等效)不起作用(WorkItems集合为null):

$items = Get-TfsItemHistory C:\myDir -recurse -stopafter 5
$items | ForEach-Object { Write-Host $_.WorkItems[0]["Title"] }

The one I would really prefer: 我真的更喜欢的那个:

$items = Get-TfsItemHistory C:\myDir -recurse -stopafter 5
foreach ($item in $items)
{
    $item.WorkItems[0]["Title"]
    # do lots of other stuff
}

I read an article about the difference between the 'foreach' operator and the ForEach-Object Cmdlet, but that seems to be more of a performance debate. 我读了一篇关于'foreach'运算符和ForEach-Object Cmdlet之间差异的文章,但这似乎更像是一场性能争论。 This really appears to be an issue about when the piping is being used. 这似乎是关于何时使用管道的问题。

I'm not sure why all three of these approaches don't work. 我不确定为什么这三种方法都不起作用。 Any insight is appreciated. 任何见解都表示赞赏。

This is indeed confusing. 这确实令人困惑。 For now a work-around is to grab the items like so: 现在,解决方法是抓住这样的项目:

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

This accesses the WorkItems collection which seems to cause this property to be populated (I know - WTF?). 这会访问WorkItems集合,这似乎会导致填充此属性(我知道 - WTF?)。 I tend to use @() to generate an array in cases where I want to use the foreach keyword. 在我想使用foreach关键字的情况下,我倾向于使用@()生成数组。 The thing with the foreach keyword is that it will iterate a scalar value including $null. 使用foreach关键字的事情是它将迭代包含$ null的标量值。 So the if the query returns nothing, $items gets assigned $null and the foreach will iterate the loop once with $item set to null. 因此,如果查询没有返回任何内容, $items将被赋予$ null,并且foreach将迭代循环一次,并将$item设置为null。 Now PowerShell generally deals with nulls very nicely. 现在PowerShell通常非常好地处理空值。 However if you hand that value back to the .NET Framework, it usually isn't as forgiving. 但是,如果将该值交还给.NET Framework,通常不会那么宽容。 The @() will guarantee an array with with either 0, 1 or N elements in it. @()将保证其中包含0,1或N个元素的数组。 If it is 0 then the foreach loop will not execute its body at all. 如果它为0,那么foreach循环将根本不执行它的主体。

BTW your last approach - foreach ($item in $items) { ... } - should work just fine. BTW你最后的方法 - foreach ($item in $items) { ... } - 应该可以正常工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM