Командлет foreach object в конвейере команд в позиции 2 укажите значения для следующих параметров
Как использовать циклы While и Foreach в Powershell Foreach на примерах
Основное предназначение циклов в Powershell, так же как и в других языках, выполнение итераций. Проще говоря они помогают выполнять одну и ту же инструкцию для разных значений. Отличительной чертой циклов в Powershell является их количество, всего их 7:
Каждый из них по своему выполняет итерации. Само понятие итераций обозначает повторное выполнение действий, например команд. Иногда можно услышать слова «итерируемый объект» или «итератор», которые обозначают объект проходящий через цикл.
Навигация по посту
Foreach-Object
Foreach-Object относится к командам, а не циклам. Чаще всего мы с ним работаем через конвейер.
Итерации
Так же, как и во многих других командах Powershell у Foreach-Object есть параметр InputObject, через который помещается объект для перебора. На примере ниже этот объект в виде массива с числами:
Если вы имеете опыт работы с Powershell, то чаще использовали с конвейером:
По сути в команде выше происходит то же самое, просто в функциях и командах есть возможность установить параметр, который принимает значения по умолчанию из конвейера. Я так же добавил вывод только имени, так как у объекта из Get-Service есть такое свойство. Более подробно это описывалось статьей по созданию команд и функций в Powershell. Параметры, которые принимают значения из конвейера можно увидеть так:
Когда вам нужно использовать больше логики зажимайте shift + enter для перехода на новую строчку, если работаете из консоли. Скрипт ниже получает список процессов, передает их через конвейер, где через наш командлет происходит анализ время отклика CPU с выводом результата:
Можно использовать и другой синтаксис. Так я получу имена сервисов:
Я не видел, что бы кто-то использовал такой синтаксис, но он возможен. На примере ниже я преобразую строки в массив двумя способами:
Сохранение через переменные
Результат этого командлета, так же как и любого другого можно сохранить в переменную и использовать дальше:
Алиасы
У этой команды есть алиасы foreach и знак %:
Обратите внимание, что использование следующего синтаксиса приведет к ошибке:
Связано это с тем, что в Powershell работает командлет и метод с одним именем. Каждый из них имеет свой синтаксис и вызывая foreach без конвейера мы используем метод. Он будет разобран следующим.
ForEach
Этот цикл, в отличие от предыдущего командлета, не будет работать через конвейер. Если вы попытаетесь использовать этот метод через конвейер, то будет использован командлет.
Это самый простой цикл Powershell, который переходит от одного значения объекта к другому. Синтаксис следующий:
Для примера с foreach в Powershell выполним перебор значений массива:
Так же как и в предыдущем случае каждый новое действие в ScriptBlock должно выполняться с новой строчки:
Выше было написано, что мы не можем использовать конвейер, но исключение такое написание:
Использование с командами
Как уже писалось выше не стоит использовать метод Powershell foreach через конвейер. Например так мы можем получить список PSProvider:
Можно добавлять выражение в сам цикл, но такой подход как минимум понизит читаемость кода. Самое главное экранировать выражение круглыми скобками:
На примере ниже пример использования цикла и командлета с одни и тем же результатом:
Работа с диапазоном или range
В Powershell можно легко указать диапазон численных значений. Я могу создать массив из чисел с 1 до 10 так:
Так же можно использовать и в итерациях:
Continue и break
Одним из отличий работы с foreach от аналогичной команды является возможность использования contnue и break.
Оператор continue минует выполнение оставшейся части скрипта после своего выполнения. На примере ниже есть два массива и если в одном из них будут встречаться одинаковые числа, то они не будут выведены:
В отличие от continue break останавливает итерации полностью. Его удобно использовать, когда мы ищем нужное значение и нам нужно остановить итерации после его нахождения:
Вложенные
Когда у нас есть массив массивов может потребоваться использовать вложенные циклы Powershell. Само их использование не должно вызывать труда, но если вы попробуете использовать операторы break и continue это сработает иначе:
Как вы видите на примере выше у нас остановился только внутренний цикл. Если нужно избежать таких ситуаций используйте OUTER:
Переменные
В этом типе цикла доступны три специальных переменных:
Такие возможности есть во многих языках программирования, но мне тяжело вспомнить ситуации что бы я их использовал. Для примера ниже мы используем массив чисел с 1 по 10. Самое первое число 1 будет выведено, после чего будет пропущено следующее:
$current просто выведет ту же переменную, объявленную в цикле:
Сравнение ForEach и команды ForEach-Object
Foreach | ForEach-Object |
---|---|
Загружает все элементы коллекции | Загружает только один элемент в память через конвейер |
Использует больше памяти из-за полной загрузки массивов | Меньшее использование памяти из-за одного элемента |
С небольшим объемом массивов работает быстрее | Работает медленнее |
Нельзя использовать через конвейер. Это не приведет к ошибке, но будет использован алиас командлета | Можно использовать конвейер или параметр InputObject |
Поддерживаются методы break и continue | Нельзя прервать используя методы contiinue и break |
Скорость работы обоих этих методов можно увидеть через следующие скрипты:
Метод foreach()
В версии Powershell 4.0 появился метод foreach() для поддержки DSC. Он немного отличается синтаксисом и подходом от описаны выше. Более простой способ понять, как он работает это посмотреть на его синтаксис:
Как я прочитал этот метод предназначен для работы только с коллекциями и по идеи такой способ должен привести к ошибке:
На всякий случай я бы советовал преобразовывать такие данные в массив:
Работа с командами
Работы с командами не должна вызывать сложности. Для примера так мы получим список имен сервисов, которые остановлены:
Самое главное экранировать результат команды в скобки или выполнять метод для переменной, которая уже хранит значения.
Еще один пример с сервисами:
В Powershell есть еще один способ итераций через for. Его отличие в том, что мы можем изменять основной объект до выполнения ScriptBlock. Синтаксис следующий:
Для примера получим числа с 1 по 10:
Вы можете изменять несколько объектов:
В итерациях вы можете использовать любой тип данных, не только цифры:
Вы можете пропустить любую часть этого цикла, но если ничего не будет указано получится бесконечный цикл:
Возможно использовать этот цикл с командлетами, но пример высосан из пальца:
Операторы break и continue работают так же.
Powershell экспорт и запись в CSV файл
While
Как можно было увидеть выше мы рассматривали циклы, которые перебирали элементы массивов, но существует еще один тип позволяющий управлять этим процессом более гибко. Для примера нам может понадобится выполнять функцию, командлет или скрипт до тех пор пока мы не получим нужны результат и для этого можно использовать While:
Все эти операции можно выполнить и с foreach, но это плохая практика. Например так можно реализовать аналогичный вечный цикл с for:
Break и continue
Ключевой момент работы с while это использование break, который остановит итерации. Мы пинговали сайт, но останавливали итерации руками и что бы этого не делать в дальнейшем нужно добавить условие при котором будет выполнен break:
Очень важно хорошо продумывать логику остановки скрипта. Вечные циклы, которые могут работать в полноценных сриптах, программах и планировщиках будет очень тяжело отлавливать.
Такие итерации останавливают либо методом break, либо счетчиком, который был показан в первом примере и ниже. Если мы будем использовать счетчик и continue, который пропускает выполнения условия, то тоже можем получить вечный цикл:
Я не помню, что бы я хоть раз использовал continue в реальных задачах, но стоит помнить о существовании такой возможности.
Мы можем выполнять команду в условиях и объявлять в ней переменную для дальнейшего использования:
Do и While
Или поставим заведомо ложные условия при котором предыдущий цикл не стал бы работать:
Такие итерации можно использовать со случаями, когда от пользователя ожидается выбор действий:
Если вам интересны или вы не понимаете switch, то я бы советовал почитать «Как работать с Powershell Switch на примерах».
Do Until
Foreach — из книги PowerShell in depth
Глава 19.3. В ней описывается конструкция Foreach, как ее применять.
Эта конструкция имеет те же цели что и командлет ForEach-Object. Командлет ForEach-Object имеет алиас ForEach, который легко спутать с оператором ForEach… потому что они имеют абсолютно одинаковые имена. PowerShell смотрит на контекст чтобы выяснить, какой же Foreach применяется сейчас. Вот пример того, как оператор, и командлет делают одно и тоже
Разберем как работает оператор Foreach, в нем есть две переменные в скобках, разделенные ключевым словом in. Вторая переменная, как ожидается содержит один или несколько объектов с которыми мы хотим что то сделать. Первая переменная для внутреннего использования, она будет содержать по очереди в каждом проходе объект из второй переменной. Если вы писали на VBScript то такого рода вещи должны выглядеть для вас знакомыми.
Иногда, вы можете быть не уверены какую конструкцию использовать — оператор или командлет. Теоретически передача по конвееру на Foreach-object может использовать меньше памяти в некоторых ситуациях. По нашим наблюдениям командлет работает медленнее с большими наборами объектов. Если у вас есть сложная обработка в конвеере, особенно если ведется обработка оператором Foreach внутри командлета Foreach лучше использовать полное название чтобы однозначно определить что именно исполльзуется.
Вам также нужно быть осторожнее если вы хотите сделать передачу по конвееру на другой командлет или функцию. Пример
PowerShell выдал ошибку, потому что нечего передать дальше по конвееру на Sort, но этот пример будет работать:
Другим фактором является нужно ли вам будет использовать коллекцию объектов еще раз, если вам понадобится дальше коллекция то лучше использовать оператор, чтобы не получать данные снова. Последний момент состоит в том что многие скрипты которые вы найдете в интернете являются на самом деле конвертацией скриптов VBScript, в нем постоянно приходилось перебирать коллекции объектов. Поэтому всегда стоит остановиться и подумать секунду чтобы определить наилучший подход и надо ли перебирать коллекцию вообще.
Наш совет — не используйте перебор если есть возможность этого не делать. Для примера перепишем наш код приведенный ранее по другому:
и пример сортировки будет намного лучше выглядеть если написать его так:
Если вам не нужно для чего то перебирать все объекты не делайте этого. Использование Foreach командлета либо оператора иногда является признаком того вы делаете то что не следует делать. Это конечно верно не во всех случаях, но задумайтесь может ли PowerShell сделать часть работы за вас. Не зацикливайтесь на этом вопросе, многие командлеты просто не принимают выходные данные из конвеера на параметры которые вам могут быть необходимы, в таком случае использование Foreach становится необходимостью. Важнее закончить работу чем отслеживать правильно или неправильно вы что то написали.
вставка переводчика
имеется в виду способность пошика связывать между собой параметры объектов на конвеере по именам параметров. Если есть вероятность что пош не сможет однозначно идентифицировать параметр объекта в процессе «parameter binding» то придется перебирать объекты по одному. Например WMI. Общая рекомендфция — используйте конвеерную обработку, цикл как правило, прерывает конвеер, а иногда приводит к ненужным итерациям. Например эффективнее сделать Select а затем обработку, чем if внутри Foreach и обработку
Хороший момент чтобы напомнить вам о скобках. Мы приврали когда сказали что оператору Foreach требуется две переменные. С технической точки зрения для этого нужна только одна переменная. Вторая должна содержать коллекцию объектов, которые могут находится либо в переменной как в наших примерах до этого, либо могут быть результатом выражения в скобках, например:
Эта версия труднее читается, но является абсолютно законной, устраняя необходимость хранить промежуточные результаты в переменной. Внутренние скобки вычисляются в первую очередь и производят коллекцию объектов которая передается дальше.
Циклы Powershell
Циклы необходимы для повторного выполнения одинаковых операций с использованием разных значений. Есть циклы обрабатывающие данные получаемые из конвейера (ForEach-Object) и работающие отдельно (ForEach). Рассмотрим все возможные циклы Powershell подробнее.
ForEach
Цикл ForEach служит для пошаговой переборки значений из коллекции элементов. Обычно при помощи цикла foreach перебирают элементы в массиве. ForEach является самым простым для понимания и чаще всего используемым циклом в Powershell. ForEach не работает с конвейером для этого есть ForEach-Object. Посмотрим на синтаксис ForEach.
Переменная $collection это массив определенный заранее. Переменная $item – это текущий элемент из $collection. По очереди перебираются все элементы из массива $collection. Далее в фигурных скобках обычно вызывают переменную $item обращаясь к текущему элементу коллекции. Перейдем к примерам. Рассмотрим простейший сценарий по удалению старых журналов сервера IIS.
В переменной $data я вычисляю текущую дату минус 10 дней. В $massiv указываю путь к логам C:\script\ и фильтрую список файлов с датой последней записи позже 10 дней от текущей даты. Далее в цикле foreach удаляю каждый файл из переменной $massiv. Скрипт довольно простой но в тоже время полезный.
Рассмотрим пример попроще. Создадим переменную с массивом чисел и в цикле к каждому числу прибавим 10.
После прохода цикла foreach к каждому числу в массиве $test прибавится 10.
Циклы Powershell foreach являются самыми распространенными.
ForEach-Object
Цикл ForEach-Object выполняет операцию над каждым элементом в коллекции входных объектов. Входные объекты передаются командлету ForEach-Object по конвейеру или могут быть заданы с помощью параметра InputObject. Цикл ForEach-Object поддерживает блоки begin, process, и end используемые в функциях.
Существует три способа построения команд в ForEach-Object. Давайте их перечислим.
Блок сценария
Блок сценария (Script block) – для задания операции используется блок скриптов. С помощью переменной $_ подставляется текущий объект. Блок сценария может содержать любой сценарий PowerShell. Рассмотрим пример с получением списка запущенных процессов.
Блоки сценария выполняются в области действия вызывающего объекта. Т.е. блоки имеют доступ к переменным в этой области и могут создавать новые переменные, которые сохраняются после завершения командлета.
Оператор выражения
Оператор выражения (Operation statement) – в данном случае вы можете использовать оператор сразу указывая значение свойства. Данный способ написания визуально более удобен и проще читается. Эта особенность впервые появилась в Windows PowerShell 3.0. Рассмотрим на примере все того же Get-Process
Как видно из примера в данном случае вывод абсолютно такой же как и в случае написания блока сценария.
Сегодня мне поступила задачка, по списку имен сотрудников (список в текстовом файле) вывести соответствующие почтовые адреса из Microsoft Exchange. Делается это одной строкой как раз с использованием ForeEach-Object.
На входе у меня список сотрудников в текстовом файле sotr.txt. Считанную информацию из файла передаю по конвейеру циклу ForeEach-Object. В цикле командлет Get-Mailbox поочередно для каждого сотрудника из файла считывает информацию и на выходе командлет ft (алиас Format-Table) выводит таблицу с данными: ФИО – EMAIL
Блок сценария (параллельный запуск)
Блок сценария (параллельный запуск) – это новая функция доступна с версии Windows Powershell 7.0 позволяет запускать блоки сценария параллельно. Используя параметр ThrottleLimit можно ограничить количество одновременно работающих скриптов. В данном случае как и раньше используется переменная $_ для подстановки текущего входного объекта. Используйте $using для передачи ссылок на переменные в запущенный скрипт.
В Microsoft Windows PowerShell 7 для каждой итерации цикла создается новое пространство выполнения, обеспечивающее максимальную изоляцию. В связи с этом нужно четко понимать что объем обрабатываемых данных не займет все ресурсы системы. Если объем данных по циклу большой используйте ThrottleLimit. Это позволит ограничить нагрузку на систему и сохранить работоспособность других сервисов. Благодаря параллельному запуску сценарий будет отрабатываться значительно быстрее.
Давайте посмотрим на тестовый скрипт с использованием параллельного запуска.
Я получаю список всех файлов и передаю их на вход ForEach-Object. Цикл параллельно (10 проходов за раз) проходит по каждому файлу и в случае превышения размера более 100 байт записывает его в файл. Это создает хорошую нагрузку на систему. Давайте посмотрим разницу в загруженных ресурсах.
Скрипт отработал 1 минуту и за это время при параллельной обработке памяти затрачено 1 ГБ против 118 Мб при последовательной. Это практически в 10 раз больше. Стоит ли скорость отработки затраченным ресурсам решать вам.
While
Цикл While – это языковая конструкция выполняющая команды в командном блоке, пока условие верно. While довольно прост в написании, давайте посмотрим на синтаксис.
Вначале While оценивает условие в круглых скобках, если оно верно (True) следует выполнение блока команд в фигурных скобках. После первого прохода снова происходит проверка условия и так бесконечно (если его не прервать операторами выхода). Цикл While завершает свою работу если условие становится не верным (False).
Возьмем простейший пример. Введем новую переменную $a и присвоим ей значение 1. Далее цикл while будет проверять значение $a и пока оно не будет равно 10 выполняется блок команд. В блоке к $a будет прибавляться 1 с каждым проходом цикла. В момент когда $a станет равно 10 цикл остановится.
Можно записать цикл и одной строкой, однако читать уже не так удобно
В данном случае я не задал переменную $a и изначально она пустая. Но с каждой итерацией цикла к ней прибавляется 1.
Еще один пример, пригодится в жизни. Напишем сценарий постоянно проверяющий запущен ли процесс. Если процесс запущен то ничего не делать, если не запущен то запустить.
В данном случае while проверяет $a=1, если да то выполнить набор команд. Но $a у меня всегда 1 поэтому цикл будет бесконечный. Это простой пример скрипта для контроля запущенного процесса.
Do работает с циклом While или Until для использования операторов в блоке скрипта в зависимости от условия. Разница между While и Do-While в том, что блок скрипта в цикле Do всегда выполняется как минимум один раз.
В цикле Do-While условие вычисляется после выполнения блока скрипта. Так же как в While блок скрипта повторяется до тех пор, пока условие оценивается как верное.
Цикл Do-Until выполняется минимум один раз перед вычислением условия. Однако блок скрипта запускается когда условие ложно. Когда условие станет верным цикл Do-Until завершит работу.
Данный скрипт проверяет запущен ли блокнот. Если не запущен выполняется условие в фигурных скобках после Do. Цикл выполняется до тех пор пока блокнот не запустят.
Continue и Break
Операторы Continue и Break работают со всеми типами циклов, кроме ForEach-Object. Они могут работать с метками. Метка – имя которое можно присвоить оператору. Формат задания меток :metka цикл(условие) <блок скрипта>.
Оператор Continue предоставляет возможность выхода из текущего блока управления. После выхода из блока цикл продолжит выполнение. В момент вызова Continue текущая итерация цикла завершается и цикл продолжится со следующей итерацией.
Рассмотрим на примере цикла While. Командлет Get-Process ищет запущенные процессы notepad. Если запущен один процесс notepad то сработает Continue и цикл продолжится со следующей итерацией. Когда запущено более 1 процесса notepad все процессы с данным именем будут закрыты.
Оператор Break позволяет выйти из текущего блока управления. Выполнение продолжается на следующем операторе после блока управления. Данный оператор очень удобен если необходимо выйти из постоянно повторяющегося цикла.
Рассмотрим пример с постоянно повторяющимся циклом, остановить который сможет только запуск процесса notepad
Операторы Continue и Break отлично дополняют циклы Powershell еще больше расширяя их возможности.
Цикл For – обычно используется для создания цикла, выполняющего команды в командном блоке пока указанное условие оценивается как верное ($True).
Рассмотрим простейший пример цикла for
В цикле я задаю переменной z значение 2. Блок команд в фигурных скобка выполняется по $z меньше или равно 20.
В данной статье я рассмотрел все циклы Powershell существующие на данный момент. Какие из них использовать в работе конечно же решать вам. До новых встреч 🙂
П.С.: задать вопросы, пообщаться, обсудить статью циклы Powershell можно у меня в VK.