PowerShell: в чем смысл ForEach-Object с InputObject? - PullRequest
2 голосов
/ 28 октября 2019

Документация для объекта ForEach гласит: «Когда вы используете параметр InputObject с ForEach-Object, а вместо команды конвейера получается ForEach-Object, значение InputObject обрабатывается как одиночноеобъект «. Такое поведение легко наблюдать непосредственно:

PS C:\WINDOWS\system32> ForEach-Object -InputObject @(1, 2, 3) {write-host $_}
1 2 3

Это кажется странным. Какой смысл в «ForEach», если нет «каждого», для которого нужно «включить»? Неужели нет способа заставить ForEach-object воздействовать непосредственно на отдельные элементы массива без конвейера? в противном случае кажется, что ForEach-Object с InputObject совершенно бесполезен. Есть что-то, чего я не понимаю в этом?

Ответы [ 2 ]

2 голосов
/ 04 ноября 2019

Полезный ответ Бендера Величайшего хорошо объясняет текущее поведение.

Для подавляющего большинства командлетов direct использование параметра -InputObjectдействительно бессмысленно , и параметр следует рассматривать как подробность реализации , единственная цель которого - облегчить ввод данных в конвейер.

Есть исключения однако, такой как командлет Get-Member, где прямое использование
-InputObject позволяет вам проверять тип самой коллекции , предоставляя эту коллекцию через конвейер будет сообщать информацию о типах его элементов.

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

В этом выпуске GitHub представлен категоризированный обзор того, какие командлеты обрабатывают напрямую
-InputObject аргументы how
.


Шаг назад:

Хотя технически это является критическим изменением, имеет смысл для параметров -InputObject (или любого параметра привязки конвейера) к по умолчанию принять иперечислять коллекций, даже если они передаются по прямому аргументу , а не по конвейеру, способом, который прозрачен для реализующей команды.

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

1 голос
/ 29 октября 2019

В случае ForEach-Object или любого командлета, предназначенного для работы с коллекцией, использование -InputObject в качестве прямого параметра не имеет смысла, поскольку командлет предназначен для работы с коллекцией, которая должна бытьразвернул и обработал по одному элементу за раз. Однако я бы также не назвал параметр «бесполезным», потому что он все еще должен быть определен, чтобы его можно было разрешить для ввода через конвейер.

Почему это так?

-InputObject - это условное имя общего параметра для того, что следует считать входным конвейером. Это параметр с [Parameter(ValueFromPipeline = $true)], установленным на него, и как таковой он лучше подходит для получения входных данных из конвейера. скорее принят в качестве прямого аргумента. Основным недостатком передачи его в качестве прямого аргумента является то, что коллекция не гарантированно будет развернута и может демонстрировать некоторые другие действия, которые могут быть не предназначены. На странице about_pipelines, указанной выше:

Когда вы передаете несколько объектов в команду, PowerShell отправляет объекты команде по одному . Когда вы используете параметр команды, объекты отправляются как один объект массива. Это незначительное различие имеет значительные последствия.

Чтобы объяснить приведенную выше цитату другими словами, передача коллекции (например, массива или списка) через конвейер автоматически развернет коллекцию и передаст ееследующая команда в конвейере по одному. Командлет сам не развертывает -InputObject , данные доставляются по одному элементу за раз. Вот почему вы можете столкнуться с проблемами при прямой передаче коллекции параметру -InputObject - поскольку командлет, вероятно, не предназначен для развертывания самой коллекции , он ожидает, что каждый элемент коллекции будет передан ему в видепоштучно.

Рассмотрим следующий пример:

# Array of hashes with a common key
$myHash = @{name = 'Alex'}, @{name='Bob'}, @{name = 'Sarah'}

# This works as intended
$myHash | Where-Object { $_.name -match 'alex' }

Приведенный выше код выводит следующее, как и ожидалось:

Name                           Value
----                           -----
name                           Alex

Но если вы передаете хеш как InputArgument прямо так:

Where-Object -InputObject $myHash { $_.name -match 'alex' }

Возвращает всю коллекцию, потому что -InputObject никогда не развертывался так, как при передаче через конвейер, но в этом контексте $_.name -match 'alex' по-прежнему возвращает true. Другими словами, при предоставлении коллекции в качестве прямого параметра для -InputObject, она обрабатывается как отдельный объект, а не выполняется каждый раз для каждого элемента в коллекции . Это также может дать видимость работы должным образом при проверке на ложное условие для этого набора данных:

Where-Object -InputObject $myHash { $_.name -match 'frodo' }

, который в итоге ничего не возвращает, потому что даже в этом контексте frodoне является значением какого-либо из ключей name в коллекции хэшей.

Короче говоря, если что-то ожидает, что ввод будет передан в качестве ввода конвейера, обычно, если не всегда, более безопасная ставкасделать это таким образом, особенно при передаче в коллекцию. Однако если вы работаете с не-коллекцией, то, скорее всего, не возникнет проблем, если вы решите использовать параметр -InputObject напрямую.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...