Тариф успешно добавлен в корзину
В корзину
url image

Циклы в bash

При составлении скриптов часто требуется повторять последовательность действий с объектами, имеющими общие признаки (расположение, имя, размер), либо выполнять такие действия, пока истинно конкретное условие. 

В языке bash, как и в большинстве языков программирования, для этих целей существует управляющая конструкция — цикл.

В статье рассмотрим все три типа циклов bash: forwhile и until, а также команды для управления ходом цикла — break и continue.

Цикл for

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

Синтаксис цикла for в общем виде: 

for переменная in массив
do
    команда 1
    команда 2
    команда n
done

Слово массив выше обозначает любой объект, по которому цикл может совершать итерации (обходить объект) и который представляет собой массив (строки и/или числа) либо последовательность (только числа) или указывает его расположение:

Объект обхода и синтаксис 

Пример команды или скрипта

Пояснение

Cлова, разделённые пробелами

 

for word in word1 word2 word3; do

    команда 1

    команда 2

done

for term in "builtin" "control operator" "exit status" "filename"; do

    echo "Термины, используемые в UNIX: $term"

done

Результат имеет желаемый вид: 

Термины, используемые в UNIX: builtin 

Термины, используемые в UNIX: control operator 

Термины, используемые в UNIX: exit status 

Термины, используемые в UNIX: filename

Чтобы оператор for распознал слова как самостоятельные элементы массива, между ними должны быть пробелы. Группа слов, заключённая в кавычки, рассматривается как один элемент.

Массивы 

("слово 1" "слово 2" "слово 3")

 

terms=("job" "name" "signal" "token")

 

for term in "${terms[@]}"; do

echo "Термины UNIX: $term"

done

 

Выводимый результат:

Термины UNIX: job

Термины UNIX: name 

Термины UNIX: signal 

Термины UNIX: token

Также слова можно включать в массивы. Как и в случае выше, группа слов, заключённая в кавычки, рассматривается как один элемент массива.

Вывод команд 

$(command)

for entry in $(cut -d: -f1,4 /etc/passwd); do

    user=$(echo "$entry" | cut -d: -f1)

    group_id=$(echo "$entry" | cut -d: -f2)

    group_name=$(getent group "$group_id" | cut -d: -f1)

    echo "Пользователь $user: основная группа $group_name"

done

Выводимый результат:

Пользователь root: основная группа root

Пользователь daemon: основная группа daemon

Пользователь mysql: основная группа mysql

...

Скрипт выполняет с каждой строкой файла /etc/passwd следующие действия:

  1. извлечь из файла значения в столбцах 1 (имя пользователя) и 4 (GID — ID основной группы);
  2. присвоить переменной user значение имени пользователя в столбце 1 этого файла;
  3. присвоить переменной group_id значение GID в столбце 2 этого файла;
  4. присвоить переменной group_name значение в первом столбце файла /etc/group, извлечённое при помощи команды getent с использованием GID;
  5. вывести сообщение с именем пользователя и его основной группы.

Пути к файлам 

/path/*

for file in /home/backups/*.gz; do

if ! gunzip -t "$file"; then

     rm "$file" && echo "Повреждённый архив $file удалён."

fi

done

Скрипт проверяет резервные копии (архивы файлов формата .gz) в каталоге на наличие повреждений, удаляет повреждённые архивы и выводит сообщение о каждом удалённом архиве.

Последовательности чисел 

{x..y}

for num in {11..19}; do

echo "$num в квадрате = $((num * num))"

done

 

Выводимый результат:

11 в квадрате = 121

12 в квадрате = 144

...

Скрипт возводит числа указанной последовательности в квадрат.

Цикл while 

Цикл while требует, чтобы указанный код продолжал выполняться, пока условие истинно. 

В общем виде синтаксис цикла while выглядит так: 

while [ условие ]; do
    команда 1
    команда 2
done

Вот пример использования цикла while для работы таймера:

#!/bin/bash
echo "Введите количество секунд для отсчёта: "
read duration
while [ $duration -gt 0 ]; do
    echo -ne "Осталось секунд: $duration\r"
    sleep 1
    ((duration--))
done
echo -e "\nВремя вышло!"

Скрипт выполняет следующие действия:

  1. Пользователю предлагают ввести значение переменной duration — число секунд для отсчёта.
  2. Пользователь вводит значение, и цикл запускается.
  3. Согласно условию [ $duration -gt 0 ]пока переменная duration больше нуля, на экране отображается надпись: 

Осталось секунд: <текущее значение переменной duration>

Затем курсор перемещается в начало этой же строки (\r — литерал (сочетание спецсимволов) для возврата к началу  строки).

  1. Команда sleep 1 вызывает бездействие оболочки на одну секунду.
  2. Количество секунд уменьшается на единицу.
  3. При новом проходе цикла надпись с изменённым количеством секунд отображается на той же строке.
  4. Когда переменная duration становится равна нулю, на новой строке (\n — литерал новой строки) отображается надпись: 

Время вышло!

Цикл until 

Цикл until продолжает работу, пока условие ложно.

В общем виде синтаксис цикла until выглядит так: 

until [ условие ]; do
    команда 1
    команда 2
done

Вот версия таймера с использованием этого вида цикла:

#!/bin/bash
echo "Введите количество секунд для отсчёта:"
read duration
until [ $duration -eq 0 ]; do
    echo -ne "Осталось секунд: $duration\r"
    sleep 1
    ((duration--))
done
echo -e "\nВремя вышло!"

Скрипт работает почти так же, как в примере для цикла while. Различие — в­ пункте­ 3: согласно условию [ $duration -eq 0 ]пока переменная duration не станет равна нулю, на экране отображается надпись: 

Осталось <текущее значение переменной duration> секунд.

Затем курсор перемещается в начало этой же строки.

Управление ходом выполнения цикла

Команды break и continue позволяют менять ход цикла: 

  • break прерывает цикл;
  • continue позволяет прервать текущую итерацию и начать следующую.

Команда break

Команда break используется для выхода из любых циклов. Для первых трёх типов цикла этот приём можно применять, когда нельзя предсказать, сколько раз будет выполняться цикл — например, если число итераций зависит от введённого пользователем значения. 

Если команда break не имеет аргументов, она прерывает выполнение того цикла, в котором присутствует. Если ей передать аргумент:

break [n]

будет прерван цикл, расположенный на n-1 уровней выше текущего, так как n текущего уровня — 1.

Следует помнить, что break завершает цикл, а не скрипт. Это можно проверить, добавив в конец скрипта команду echo

Общий вид синтаксиса:

for [ условие ]; do 
    команда 1
    команда 2
    if [ condition ]; then
      break
    fi
    команда3
done

Команда continue

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

Общий вид синтаксиса:

for [ условие ]; do
   if [ условие 2 ]
       then
       continue
   fi
   команда
done

Как правило, предварительная фильтрация данных вне цикла и последующая их обработка в цикле выполняются быстрее, чем проверка условия внутри цикла и прерывание итерации при помощи continue

Но в некоторых случаях вариант с применением continue разумнее — например, если в скрипте используется файл, уже открытый другим процессом. Если в файл в данный момент ведётся запись, его обработка может привести к возникновению ошибок сжатия.

В следующем скрипте цикл сначала проверяет, закрыт ли файл резервной копии (дампа) базы данных, и если закрыт, то архивирует его и перемещает в каталог с архивами.

#!/bin/bash

backup_dir="/opt/mysql/backups"
archive_dir="/opt/mysql/backup-archives"
mkdir -p "$backup_dir" "$archive_dir"

for dump in "$backup_dir"/*.dump "$backup_dir"/*.sql; do
    lsof "$dump" &>/dev/null && continue
    gzip "$dump"
    mv "$dump.gz" "$archive_dir"
done

Скрипт выполняет следующие действия:

  1. В переменную backup_dir записывается путь к каталогу c дампами БД, в переменную archive_dir — путь к каталогу с архивами.
  2. Если указанные каталоги не существуют, они создаются командой mkdir.
  3. Цикл for обходит в каталоге, обозначенном переменной backup_dir, все файлы с именем, заканчивающимся на .sql или .dump. Обработка каждого элемента обозначается итерацией с именем dump.

В ходе каждой итерации совершаются следующие действия:

3.1. Команда lsof проверяет, находится ли файл в списке открытых. Если файл открыт, то итерация с ним прерывается и цикл переходит к обработке следующего файла  с именем, заканчивающимся на .sql  или .dump.

3.2. Если файла нет в списке открытых, то он архивируется командой gzip, и к исходному имени добавляется расширение .gz. Исходный файл дампа удаляется.

3.3. Команда mv перемещает созданный архив в каталог, обозначенный переменной archive_dir.

  1. Цикл завершается.

Вложенные циклы

Вложенные циклы используют для повторения набора действий с элементами на нескольких уровнях структур данных или последовательностей. Путём обращения к элементам массивов и древовидных структур, таких как каталоги или json-файлы среднего размера, можно вести поиск, фильтрацию и сортировку этих элементов. 

Для работы с большими файлами или несколькими уровнями вложенности лучше использовать специализированные инструменты — например, Python и его библиотеки simplejson, ujson, pandas

Вложенные циклы могут быть любых типов — for, while или until. Чаще всего встречаются вложенные циклы for.

Во вложенном цикле for внешний цикл управляет обходом первого массива данных, а внутренний цикл для каждого элемента массива во внешнем цикле обходит второй массив данных.

Общий вид синтаксиса цикла for:

for [ условие 1 ]; do
   for[ условие 2 ]; do
       команда 1
   done
   команда 2
done

 

Мы рассмотрели типы циклов bash: forwhile и until, а также способы управления циклами — команды break и continue.

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

 

Автор статьи: Клара Суботэ

Этот материал был полезен?

Скидка новым клиентам
Закажите сервер сегодня и получите скидку на первый месяц аренды!
Фаина-236 Ваш виртуальный бортовой помощник
Маршрут «Марс-Земля» построен. Готовы к старту? Участвуйте в акции и получайте призы!