Циклы PowerShell: foreach и ForEach-Object
В статье рассказали про циклы для настройки автоматизации в PowerShell.
Продолжаем ликбез по PowerShell. В этой части расскажем про важный элемент автоматизации — циклы.
В предыдущей статье мы рассказывали про фундаментальные основы PowerShell. Рекомендуем начинать знакомство с PowerShell с него. Также ранее подсветили различия между командной строкой CMD и PowerShell.
Методы работы с массивами PowerShell
Цикл — это управляющая конструкция, позволяющая проводить однотипных набор операций над одним или несколькими элементами. Такой набор элементов называется массив (array) или список (list).
Множество командлетов возвращает массив объектов. Например, командлет Get-Service создает массив объектов, описывающих службы Windows. В некоторых случаях результат работы командлета необходимо сохранить в переменную. Сделать это можно следующим образом:
$result = Test-Connection selectel.ru
Данная команда сохраняет результат работы командлета Test-Connection в переменную с именем result. Имя переменной должно состоять только из букв, цифр, знака вопроса и дефиса. Если этого недостаточно, то его можно заключить в фигурные скобки. Этот метод позволит использовать в качестве имени практически любые символы Юникода, кроме закрывающей фигурной скобки и грависа (`). Например:
# Синтаксическая ошибка
$переменная с пробелом = Get-Service
# Все ок!
${переменная с пробелом} = Get-Service
Создание константного массива: от числового значения до оператора Range
В переменные также можно помещать константные значения. Для создания константного массива есть несколько вариантов синтаксиса.
# Числовое значение
$num = 123
# Строковое значение
$word = “string”
# Массив, способ 1
$arr1 = @(“word1”, “word2”)
# Массив, способ 2
$arr2 = “word1”,”word2”
# Массив из последовательности чисел от 1 до 10 (оператор Range)
$arr3 = 1..10
Чтобы вывести содержимое переменной на экран, достаточно написать ее название со знаком доллара в начале. Обращение к элементу массива производится через указание индекса (номера) элемента в квадратных скобках. Обратите внимание, нумерация начинается с нуля!
# Выведет весь массив на экран
$arr1
# Выведет word2
$arr1[1]
Теперь, когда мы умеем работать с массивами, перейдем к циклам.
Циклы PowerShell
Командная оболочка PowerShell поддерживает целую коллекцию циклов. Рассмотрим каждый из них.
Цикл с предусловием: PowerShell While
Цикл с предусловием — это цикл, в котором условие выполнения цикла проверяется до первой итерации. PowerShell While имеет следующий синтаксис:
while (условие) {тело цикла – команды}
Например, печатаем на экран сообщение Hello, World! раз в секунду, пока секундная стрелка часов показывают от 0 до 30 секунд:
while((Get-Time).Second -le 30) {
Write-Host “Hello, World!”
Start-Sleep 1
}
Обратите внимание на операцию сравнения, которая задана ключом le. Этот ключ обозначает less or equal.
Ключ | Расшифровка | Символьная форма (не поддерживается в PowerShell) |
lt | Less Than | < |
le | Less or Equal | <= |
gt | Greater Than | > |
ge | Greater or Equal | >= |
eq | EQual | == |
ne | Not Equal | != |
В использовании цикла While есть особенность: если условие не выполняется, то тело цикла не будет выполнено. Таким образом, он не выведет ни одной строчки, если будет запущен между 31 и 60 секундой.
Если необходимо выполнить тело цикла как минимум один раз, следует использовать цикл с постусловием.
Цикл с постусловием: do-while и do-until
Цикл с постусловием — это цикл, в котором условие выполнения цикла проверяется после первой итерации. В PowerShell доступны две версии цикла с постусловием:
- пока условие истинно —
do {тело цикла – команды} while (условие);
- пока условие ложно —
do {тело цикла – команды} until (условие)
Перепишем пример из предыдущего пункта на циклы с постусловием.
# Этот цикл будет повторяться, когда время между 0 и 30 секундой
do {
(Get-Date).Second
Start-Sleep 1
} while ((Get-Date).Second -le 30)
# Этот цикл будет повторяться, когда время между 31 и 60 секундой
do {
(Get-Date).Second
Start-Sleep 1
} until ((Get-Date).Second -le 30)
Обратите внимание, что тело каждого цикла выполнится как минимум один раз. Циклы с предусловием и постусловием актуальны, когда не требуется счетчик.
Цикл со счетчиком
В некоторых случаях необходимо использовать значение счетчика. Например, вывести все квадраты чисел от 0 до 10. Синтаксис цикла со счетчиком выглядит следующим образом:
for (инициализация счетчика; условия выполнения цикла; изменение счетчика) { тело цикла}
Например, цикл для вывода квадрата чисел:
for($d=0; $d -le 10; $d = $d + 1) {
Write-Host ($d*$d)
}
Результат работы:
PS C:\Users\sun> for($d=0; $d -le 10; $d = $d + 1) { write-host ($d*$d) }
0
1
4
9
16
25
36
49
64
81
100
Впрочем, иногда цикл со счетчиком используется для более лаконичной записи циклов с предусловием. Допустим, задача вывести Hello, World! ровно 10 раз.
Цикл со счетчиком | Цикл с предусловием |
for($i = 0; $i -lt 10; $i = $i + 1) { | $i = 0 |
Write-Host “Hello, World” } | while($i -lt 10) { Write-Host “Hello, World” $i = $i + 1 } |
Циклы со счетчиком можно использовать для обработки массивов. Например, вывести квадраты чисел, которые содержатся в массиве.
$arr = 1,1,2,3,5,8,11
for ($i = 0; $i -lt $arr.length; $i++) {
$obj = $arr[$i];
Write-Host ($obj*$obj)
}
Это правильный способ перебора всех элементов массива, но в нем не хватает лаконичности. Мы использует цикл со счетчиком, но счетчик используется только для получения соответствующего элемента массива. Можно ли сделать это лаконичнее? Конечно, можно.
Используйте PowerShell в работе с выделенными серверами
Более 100 готовых конфигураций и быстрое развертывание
Команда foreach
Цикл foreach — это лаконичная версия цикла со счетчиком для обхода массивов. У лаконичности есть цена: доступ к счетчику невозможен. Теперь вывод квадратов из массива чисел выглядит следующим образом.
$arr = 1,1,2,3,5,8,11
foreach($obj in $arr) {
Write-Host ($obj*$obj)
}
Синтаксис такого цикла выглядит следующим образом:
foreach (<имя_переменной> in <имя массива>) {
тело цикла
}
Казалось бы, на этом можно завершать, но, как известно, нет предела совершенству.
В PowerShell есть конвейеры, которые позволяют передавать объекты с выхода одного командлета на вход другому командлету. В конвейере объекты передаются по готовности, а цикл foreach работает только после формирования массива.
foreach($obj in Test-Connection 1.1.1.1) {
Write-Host $obj
}
Такой код сначала дождется выполнения командлета Test-Connection, а уже после начнется выполнение тела цикла. Этот момент можно оптимизировать.
Командлет PowerShell ForEach-Object
Для итерации по результату работы команды следует использовать командлет ForEach-Object, который имеет псевдоним foreach. Как PowerShell их различает? По синтаксису и местоположению.
Синтаксис командлета ForEach-Object:
другой командлет | ForEach-Object {тело цикла}
Командлет ForEach-Object не содержит выражения в круглых скобках и обязательно следует в конвейере после другого командлета. В тело цикла передается переменная с именем нижнее подчеркивание ($_), которая является текущим объектом.
Примеры конвейеров с ForEach-Object:
Test-Connection 1.1.1.1 | Foreach-Object {$_.address; start-sleep -milliseconds 100}
Test-Connection 1.1.1.1 | foreach {$_.address; start-sleep -milliseconds 100}
Test-Connection 1.1.1.1 | % {$_.address; start-sleep -milliseconds 100}
Использование командлета может значительно ускорить время выполнения блока сценария.
Ключевые слова continue и break
С простыми циклами все понятно. Нередко в теле цикла может быть сложная логика, которая требует пропустить обработку элемента или прервать весь цикл. Для этих целей существуют специальные слова в PowerShell – continue и break соответственно:
- Ключевое слово continue прекращает текущую итерацию цикла, как будто тело цикла закончилось. После этого цикл продолжает выполняться.
- Ключевое слово break прерывает текущую итерацию и завершает цикл.
Заключение
В этом тексте мы рассмотрели самые популярные циклы PowerShell. Они необходимы для повторного выполнения одинаковых операций, то есть для автоматизации действий.