Bash While: описание циклов, примеры

Циклы позволяют автоматизировать выполнение однотипных действий в программах. Цикл while - один из базовых и полезных циклов в скриптинге Bash. Давайте разберемся с его работой.

Общие сведения о циклах while в Bash

Цикл while выполняет блок кода, пока заданное условие истинно. Синтаксис:

while [condition] do # блок кода done

Пока условие истинно, блок кода выполняется снова и снова. Часто цикл while используется для обработки файлов построчно или элементов массива.

Бесконечные циклы while true

Цикл while можно сделать бесконечным, указав условие while true. Такой цикл никогда не завершится сам по себе. Чтобы выйти из него, нужно использовать оператор break или выполнить exit.

Бесконечные циклы полезны для непрерывного мониторинга системы. Например, следить за ростом лог-файла:

 while true do current_size=$(wc -c /var/log/syslog) sleep 5 done 

Чтение и обработка файлов

Цикл while часто используется для построчной обработки текстовых файлов:

 while read line do echo $line done < файл.txt 

Файл подается на вход цикла через перенаправление < файл.txt. Команда read считывает строки по одной.

Операторы управления циклом

Для управления выполнением цикла while используются операторы break и continue:

  • break - досрочный выход из цикла
  • continue - пропуск текущей итерации цикла

Простые примеры циклов while

Цикл с переменной-счетчиком

Классический цикл с счетчиком от 0 до 4:

 i=0 while [ $i -lt 5 ]; do echo $i i=$((i+1)) done 

На каждой итерации счетчик i увеличивается на 1. Цикл выполняется, пока i меньше 5.

Цикл с прерыванием по условию (break)

 i=0 while true; do echo $i if [ $i -eq 2 ]; then break fi i=$((i+1)) done 

Как только переменная i равна 2, оператор break прервет цикл.

Бесконечный цикл с выходом по команде

 while true do read -p "Введите Q для выхода: " var if [ "$var" == "Q" ] ; then echo "Выход" break fi # действия цикла done 

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

Цикл while для чтения аргументов командной строки

Цикл while удобен для разбора аргументов скрипта:

 while [[ $# -gt 0 ]]; do case $1 in -a|--all) all="true";; -f|--file) file="$2"; shift;; *) echo "Неизвестный параметр $1"; exit 1;; esac shift done 

Переменная $# хранит количество аргументов. Цикл работает, пока аргументы есть. Оператор shift сдвигает параметры на 1.

Обработка аргументов через $1, $2 и т.д.

Доступ к конкретным аргументам осуществляется через $1, $2 и т.д:

 while [ -n "$1" ] do case "$1" in -a) echo "Найден параметр -a";; -b) arg="$2"; echo "Аргумент -b: $arg"; shift;; *) echo "Неизвестный параметр $1"; exit 1;; esac shift done 

Тут $1 - текущий рассматриваемый параметр, $2 - следующий за ним.

Проверка обязательных параметров

Можно проверить, что нужные параметры переданы:

 while [[ $# -gt 0 ]]; do # разбор параметров done if [ -z "$file" ]; then echo "Не указан обязательный параметр --file" exit 1 fi 

Если переменная $file пустая после цикла - значит параметр не задан.

Чтение данных из файла построчно

 while read line; do echo "$line" done < файл.txt 

Команда read в цикле читает файл построчно. Файл подается через перенаправление < файл.txt.

Обработка разделителей строк

Чтобы read не интерпретировал символы \\ нужно:

 while IFS= read -r line; do # обработка done < файл.txt 

Параметр IFS= отключает разделители. Флаг -r не интерпретирует экранирование.

Чтение по символам/блокам

Опции -n и -t позволяют читать:

  • -n 5 - по 5 символов
  • -t 2 - таймаут в 2 секунды

Работа с потоками

Данные в цикл можно подавать не только из файла, но и из стандартных потоков:

 # stdin while read line; do echo $line done # stdout while read line; do echo $line done < <(ls -l) 

Таким образом реализуется обработка ввода/вывода в скрипте.

Мониторинг процессов и системы

Циклы while позволяют организовать мониторинг и ожидание событий в системе.

Отслеживание изменения размера файла

 last_size=0 while true; do current_size=$(wc -c файл.txt) if [ $current_size -ne $last_size ]; then echo "Размер изменился на: $((current_size - last_size))" fi last_size=$current_size sleep 2 done 

Скрипт каждые 2 секунды проверяет размер файла и выводит изменение.

Ожидание события

 while [ ! -d /tmp/new_dir ]; do echo "Ждем появления директории /tmp/new_dir" sleep 5 done echo "Директория появилась" 

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

Получение вывода команды

 out=$(mkfifo /tmp/pipe) ssh server $(cat /tmp/pipe) & while read line; do echo "$line" done < /tmp/pipe rm /tmp/pipe 

Запускаем удаленную команду через SSH и читаем ее вывод через именованный канал.

Работа с массивами и словарями

Циклы while используются для итерации по элементам коллекций данных.

Цикл по массиву

 arr=(1 2 3 4) i=0 while [ $i -lt ${#arr[@]} ]; do echo ${arr[$i]} i=$((i+1)) done 

Переменная ${#arr[@]} содержит размер массива arr.

Обход ключей словаря

 declare -A dict=( [a]=1 [b]=2 ) for key in "${!dict[@]}"; do echo "$key: ${dict[$key]}" done 

Оператор "${!dict[@]}" возвращает все ключи словаря dict.

Добавление элементов

 sites=([google]=1 [ya]=2) while read site; do sites[$site]=0 done < файл со списком сайтов 

Цикл добавляет ключи из файла со списком сайтов в словарь.

Удаление элементов

 sites=([google]=1 [ya]=2 [bing]=3) while read key; do unset sites[$key] done < файл со списком ключей для удаления 

Цикл удаляет ключи, перечисленные в файле со списком ключей, из словаря.

Дополнительные возможности цикла while

Рассмотрим некоторые расширенные возможности цикла while в Bash.

Обработка ошибок

Чтобы перехватывать ошибки внутри цикла, используется конструкция:

 while true; do command status=$? if [ $status -ne 0 ]; then echo "Произошла ошибка" >&2 break fi done 

Переменная $? хранит код возврата последней команды. При ошибке можно выйти из цикла.

Задержка выполнения

Чтобы добавить паузу между итерациями цикла, используется sleep:

 while true; do # код цикла sleep 5 done 

Пауза в 5 секунд будет после каждой итерации.

Обработка сигналов

 trap 'exit 0' SIGINT SIGTERM while true; do # код цикла done 

Конструкция trap перехватывает сигналы SIGINT и SIGTERM, чтобы цикл корректно завершился.

Рекурсивные циклы

 #!/bin/bash function loop { while true; do # код цикла loop done } loop 

Функция loop рекурсивно вызывает сама себя, создавая бесконечный цикл.

Примеры практического применения

Пакетная обработка файлов

while read file; do convert "$file" "${file%.jpg}.png" done < files.txt

Цикл конвертирует изображения из списка в файле files.txt из jpg в png.

Мониторинг процесса

while true; do if ! ps aux | grep "[p]rocess"; then echo "Процесс остановлен" break fi sleep 5 done

Цикл следит за запущенным процессом и выводит уведомление, когда процесс завершится.

Построчная запись в файл

while read line; do echo "$line" >> file.txt done < lines.txt

Скрипт записывает строки из lines.txt в конец file.txt построчно.

Цикл while выполняет блок кода, пока заданное условие истинно. Синтаксис:

while [condition] do # блок кода done

Пока условие истинно, блок кода выполняется снова и снова. Часто цикл while используется для обработки файлов построчно или элементов массива.

Бесконечные циклы while true

Цикл while можно сделать бесконечным, указав условие while true. Такой цикл никогда не завершится сам по себе. Чтобы выйти из него, нужно использовать оператор break или выполнить exit.

Бесконечные циклы полезны для непрерывного мониторинга системы. Например, следить за ростом лог-файла:

while true do current_size=$(wc -c /var/log/syslog) sleep 5 done

Чтение и обработка файлов

Цикл while часто используется для построчной обработки текстовых файлов:

while read line do echo $line done < файл.txt

Файл подается на вход цикла через перенаправление < файл.txt. Команда read считывает строки по одной.

Операторы управления циклом

Для управления выполнением цикла while используются операторы break и continue:

  • break - досрочный выход из цикла

  • continue - пропуск текущей итерации цикла

Простые примеры циклов while

Цикл с переменной-счетчиком

Классический цикл с счетчиком от 0 до 4:

i=0 while [ $i -lt 5 ]; do echo $i i=$((i+1)) done

На каждой итерации счетчик i увеличивается на 1. Цикл выполняется, пока i меньше 5.

Бесконечный цикл с выходом по команде

while true do read -p "Введите Q для выхода: " var if [ "$var" == "Q" ] ; then echo "Выход" break fi # действия цикла done

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

Цикл while для чтения аргументов командной строки

Цикл while удобен для разбора аргументов скрипта:

while [[ $# -gt 0 ]]; do case $1 in -a|--all) all="true";; -f|--file) file="$2"; shift;; *) echo "Неизвестный параметр $1"; exit 1;; esac shift done

Переменная $# хранит количество аргументов. Цикл работает, пока аргументы есть. Оператор shift сдвигает параметры на 1.

Обработка аргументов через $1, $2 и т.д.

Доступ к конкретным аргументам осуществляется через $1, $2 и т.д:

while [ -n "$1" ] do case "$1" in -a) echo "Найден параметр -a";; -b) arg="$2"; echo "Аргумент -b: $arg"; shift;; *) echo "Неизвестный параметр $1"; exit 1;; esac shift done

Тут $1 - текущий рассматриваемый параметр, $2 - следующий за ним.

Проверка обязательных параметров

Можно проверить, что нужные параметры переданы:

while [[ $# -gt 0 ]]; do # разбор параметров done if [ -z "$file" ]; then echo "Не указан обязательный параметр --file" exit 1 fi

Если переменная $file пустая после цикла - значит параметр не задан.

Чтение данных из файла построчно

while read line; do echo "$line" done < файл.txt

Команда read в цикле читает файл построчно. Файл подается через перенаправление < файл.txt.

Обработка разделителей строк

Чтобы read не интерпретировал символы \\ нужно:

while IFS= read -r line; do # обработка done < файл.txt

Параметр IFS= отключает разделители. Флаг -r не интерпретирует экранирование.

Чтение по символам/блокам

Опции -n и -t позволяют читать:

  • -n 5 - по 5 символов

  • -t 2 - таймаут в 2 секунды

Работа с потоками

Данные в цикл можно подавать не только из файла, но и из стандартных потоков:

# stdin while read line; do echo $line done # stdout while read line; do echo $line done < <(ls -l)

Таким образом реализуется обработка ввода/вывода в скрипте.

Комментарии