Циклы Powershell - ForEach и ForEach-Object с примерами, команды while, continue и break

Циклы PowerShell: foreach и ForEach-Object

Владимир Туров
Владимир Туров Разработчик
7 декабря 2022

В статье рассказали про циклы для настройки автоматизации в 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)
ltLess Than<
leLess or Equal<=
gtGreater Than>
geGreater or Equal>=
eqEQual==
neNot 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)
}

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

Команда 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. Они необходимы для повторного выполнения одинаковых операций, то есть для автоматизации действий.