PowerShell ForEach / путаница в конвейере

Я использую командлеты TFS PowerTools в PowerShell, чтобы попытаться получить некоторую информацию о наборах изменений и связанных рабочих элементах с моего сервера. Я свел проблему к поведению, которое я не понимаю, и я надеюсь, что это не относится к TFS (так что кто-то может объяснить мне проблему :))

Вот единственная команда, с которой я могу работать:

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

Он делает то, что я ожидаю - Get-TfsItemHistory возвращает список из 5 наборов изменений и передает их в функцию foreach, которая распечатывает заголовок первого связанного рабочего элемента. Так в чем моя проблема? Я пытаюсь написать большой скрипт и предпочитаю кодировать вещи так, чтобы они больше походили на программу на C # (синтаксис PowerShell заставляет меня плакать). Всякий раз, когда я пытаюсь сделать то, что написано выше, любым другим способом, коллекция WorkItems равна нулю.

Следующие команды (которые я интерпретирую как логически эквивалентные) не работают (коллекция WorkItems равна нулю):

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

Тот, который я бы предпочел:

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

Я прочитал статью о разнице между оператором foreach и командлетом ForEach-Object, но, похоже, это скорее споры о производительности. Это действительно похоже на то, когда используется трубопровод.

Я не уверен, почему все три подхода не работают. Любое понимание приветствуется.


person hexate    schedule 24.10.2010    source источник
comment
Я не уверен, в чем проблема, но она определенно специфична для командлетов TFS (они довольно ужасны, имо). Похоже, что командлет выполнял ленивую загрузку, и как только конвейер заканчивается, контекст данных исчезает, и уже слишком поздно загружать данные, но дизайн командлетов настолько запутан, что я не мог отследить его так далеко в отражателе. .   -  person Jaykul    schedule 25.10.2010


Ответы (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) { ... } - должен работать нормально.

person Keith Hill    schedule 24.10.2010
comment
Спасибо, работает. Я снова на правильном пути, и я не сумасшедший! Это, конечно, странно, было бы неплохо узнать, в чем дело, но на данный момент я готов списать еще одну на гремлинов. - person hexate; 25.10.2010