Как работает порядок выполнения командлета ForEach-Object в конвейере?

В PowerShell кажется, что порядок выполнения командлетов в конвейере не выполняется очевидным образом. Вместо того, чтобы каждый командлет выполнялся и затем передавал результаты следующему командлету в конвейере, кажется, что отдельные выходные объекты командлета передаются на вход следующего командлета до завершения выполнения предыдущего командлета. Следующее подтверждает это поведение:

1..5 | %{ Write-Host $_; $_ } | %{ Write-Host ([char]($_ + 64)) }

отпечатки

1
A
2
B
3
C
4
D
5
E

Похоже, что происходит следующее: каждое выполнение ForEach-Object командлета будет выполнять свой блок сценария и каждую последующую команду в своем конвейере перед итерацией.

Это то, что происходит на самом деле, и задокументировано ли где-нибудь такое поведение? Это относится ко всем итеративным командлетам, таким как ForEach-Object (например, Where-Object и т. Д.)?

Я знаю, что могу заключить части в выражение ((1..5 | %{ Write-Host $_; $_ }) | %{ Write-Host ([char]($_ + 64)) }) или назначить часть переменной, а затем передать ее последующим командам конвейера, чтобы избежать такого поведения, но есть ли способ выполнить операцию с каждым элементом коллекции, а затем передать вся эта коллекция передается следующей команде в конвейере?


person Ian Pugsley    schedule 27.07.2012    source источник


Ответы (2)


Это соответствует моему пониманию того, как конвейеры обрабатывают объекты. Предметы перемещаются "как можно дальше" по конвейеру, прежде чем коснуться следующего объекта. Некоторые командлеты не используют блок «процесс», а вместо этого используют блок «конец» по необходимости (например, объект сортировки должен иметь все элементы для фактического выполнения сортировки), поэтому они блокируют конвейер. Другие используют запись-вывод (что неявно делает ваш второй $ _) и поддерживают движение конвейера.

person Mike Shepard    schedule 27.07.2012
comment
Согласился с этим; вывод вопроса мне интуитивно понятен. Обратите внимание, как изменяется ваш результат, когда вы делаете (1..5 | %{ Write-Host $_; $_ }) | %{ Write-Host ([char]($_ + 64))}. Это заставит сначала выполнить все в (), а затем каждый из этих элементов будет передан внешнему конвейеру. - person SpellingD; 28.07.2012
comment
Я помню видео (Channel 9?), В котором Джеффри Сновер и Эрик Мейер говорили о PowerShell и Monads, где Сновер нарисовал диаграмму того, как вещи проталкиваются по конвейеру. Я посмотрю, найду ли ссылку. - person Mike Shepard; 28.07.2012
comment
Найти это было несложно. Вот ссылка - person Mike Shepard; 28.07.2012
comment
Это также соответствует моему опыту и тестированию. Удалось ли вам найти какую-либо документацию об этом поведении и как узнать, блокирует ли команда конвейера конвейер или нет? - person Ian Pugsley; 30.07.2012
comment
Брюс Пайетт обсуждает это в PowerShell in Action 2nd Edition в разделе «Конвейеры и поведение потоковой передачи» (раздел 2.5.1, стр. 56-57). Если вы хотите понять механику PowerShell, я не могу порекомендовать более высокую книгу. - person Mike Shepard; 31.07.2012

Каждый объект проходит по конвейеру настолько далеко, насколько может. Вот почему каждый командлет должен записывать свой вывод непосредственно в конвейер, а не накапливать его в каком-либо внутреннем хранилище, за исключением случаев, когда он должен обрабатывать все свои входные данные вместе, как Sort-Object.

У него также есть некоторые интересные побочные эффекты. Например, командлет Get-Member дает разные выходные данные, если он получает входные данные из конвейера, и другой, если он получает свои входные данные из параметра InputObject.

И, поскольку ЭТО ЭТО PowerShell, все это задокументировано здесь:

Get-Help about_pipelines
person Thanassis    schedule 21.01.2017
comment
Это должна быть ссылка в конце? Не работает. - person Tom Zych; 22.01.2017
comment
Неа. Это команда PowerShell: Get-Help about_pipelines Если вы не обновляли справку локально, используйте Get-Help about_pipelines -Online или выполните поиск about_pipelines - person Thanassis; 22.01.2017