[Перевод] Foreach — из книги PowerShell in depth
Глава 19.3. В ней описывается конструкция Foreach, как ее применять.
Эта конструкция имеет те же цели что и командлет ForEach-Object. Командлет ForEach-Object имеет алиас ForEach, который легко спутать с оператором ForEach… потому что они имеют абсолютно одинаковые имена. PowerShell смотрит на контекст чтобы выяснить, какой же Foreach применяется сейчас. Вот пример того, как оператор, и командлет делают одно и тоже
Get-Service –name B* | ForEach { $_.Pause() }
$services = Get-Service –name B*
ForEach ($service in $services)
{
$service.Pause()
}
Разберем как работает эта оператор Foreach, в нем есть две переменные в скобках, разделенные ключевым словом in. Вторая переменная, как ожидается содержит один или несколько объектов с которыми мы хотим что то сделать. Первая переменная для внутреннего использования, она будет содержать по очереди в каждом проходе объект из второй переменной. Если вы писали на VBScript то такого рода вещи должны выглядеть для вас знакомыми.
Обычная практика именовать вторую переменную во множественном числе, а первую в единственном. Это не требуется соглашением или стандартом, хотя вы можете написать Foreach ($Fred in $Rocvill) при условии что в $Rockvill содержатся объекты, PowerShell будет рад обработать это. Но придерживайтесь осознанного именования переменных, тогда вам придется гораздо меньше запоминать.
PowerShell автоматически принимает по одному объекту из второй переменной и помещает его в первый на каждом проходе цикла. Внутри конструкции можно использовать первую переменную для того чтобы делать что то с объектом, например вы можете вызвать метод Pause этого объекта. Не используйте $_ (или PSItem) в операторе, как вы это делаете в командлете.
Иногда, вы можете быть не уверены какую конструкцию использовать — оператор или командлет. Теоретически передача по конвееру на Foreach-object может использовать меньше памяти в некоторых ситуациях. По нашим наблюдениям командлет работает медленнее с большими наборами объектов. Если у вас есть сложная обработка в конвеере, особенно если ведется обработка оператором Foreach внутри командлета Foreach лучше использовать полное название чтобы однозначно определить что именно исполльзуется.
Вам также нужно быть осторожнее если вы хотите сделать передачу по конвееру на другой командлет или функцию. Пример
PS C:\> foreach ($service in $services) {
>> $service | select Name,DisplayName,Status
>> } | Sort Status
>>
An empty pipe element is not allowed.
At line:3 char:4
+ } | <<<< Sort Status
+ CategoryInfo : ParserError: (:) [],
ParentContainsErrorRecordException
+ FullyQualifiedErrorId : EmptyPipeElement
PowerShell выдал ошибку, потому что нечего передать дальше по конвееру на Sort, но этот пример будет работать:
PS C:\> $services | foreach {
>> $_ | select Name,DisplayName,Status
>> } | Sort Status
>>
Name DisplayName Status
---- ----------- ------
Browser Computer Browser Stopped
BDESVC BitLocker Drive Encrypt... Stopped
bthserv Bluetooth Support Service Running
BFE Base Filtering Engine Running
BITS Background Intelligent ... Running
Другим фактором является нужно ли вам будет использовать коллекцию объектов еще раз, если вам понадобится дальше коллекция то лучше использовать оператор, чтобы не получать данные снова. Последний момент состоит в том что многие скрипты которые вы найдете в интернете являются на самом деле конвертацией скриптов VBScript, в нем постоянно приходилось перебирать коллекции объектов. Поэтому всегда стоит остановиться и подумать секунду чтобы определить наилучший подход и надо ли перебирать коллекцию вообще.
Наш совет — не используйте перебор если есть возможность этого не делать. Для примера перепишем наш код приведенный ранее по другому:
Get-Service –name B* | Suspend-Service
и пример сортировки будет намного лучше выглядеть если написать его так:
Get-Service b* | Sort Status | select Name,DisplayName,Status
Если вам не нужно для чего то перебирать все объекты не делайте этого. Использование Foreach командлета либо оператора иногда является признаком того вы делаете то что не следует делать. Это конечно верно не во всех случаях, но задумайтесь может ли PowerShell сделать часть работы за вас. Не зацикливайтесь на этом вопросе, многие командлеты просто не принимают выходные данные из конвеера на параметры которые вам могут быть необходимы, в таком случае использование Foreach становится необходимостью. Важнее закончить работу чем отслеживать правильно или неправильно вы что то написали.
вставка переводчика
имеется в виду способность пошика связывать между собой параметры объектов на конвеере по именам параметров. Если есть вероятность что пош не сможет однозначно идентифицировать параметр объекта в процессе «parameter binding» то придется перебирать объекты по одному. Например WMI. Общая рекомендфция — используйте конвеерную обработку, цикл как правило, прерывает конвеер, а иногда приводит к ненужным итерациям. Например эффективнее сделать Select, а затем обработку, чем if внутри Foreach и обработку
Хороший момент чтобы напомнить вам о скобках. Мы приврали когда сказали что оператору Foreach требуется две переменные. С технической точки зрения для этого нужна только одна переменная. Вторая должна содержать коллекцию объектов, которые могут находится либо в переменной как в наших примерах до этого, либо могут быть результатом выражения в скобках, например:
foreach ($service in (Get-Service –name B*)) {
$service.pause()
}
Эта версия труднее читается, но является абсолютно законной, устраняя необходимость хранить промежуточные результаты в переменной. Внутренние скобки вычисляются в первую очередь и производят коллекцию объектов которая передается дальше.