User Tools

Site Tools


bash:циклы_while_и_until

Управление потоком выполнения: циклы while и until

Циклы

Повседневная жизнь наполнена повторяющимися действиями. Каждодневная поездка на работу, прогулка с собакой и нарезание моркови — все эти действия состоят из повторяющейся последовательности действий. Рассмотрим в качестве примера резку моркови. Этот вид деятельности можно выразить на псевдокоде примерно так:

  1. Взять разделочную доску.
  2. Взять нож.
  3. Положить морковь на доску.
  4. Поднять нож.
  5. Сдвинуть морковь
  6. Отрезать кусок.
  7. Если вся морковь порезана, завершить операцию, иначе перейти к шагу 4.

Шаги с 4-го по 7-й образуют цикл. Действия внутри цикла повторяются, пока не будет выполнено условие «вся морковь порезана».

while

В bash имеются средства, позволяющие выражать похожие идеи. Представьте, что нам нужно вывести пять чисел по порядку, от 1 до 5. В сценарии на языке bash это можно реализовать, как показано ниже:

#!/bin/bash
 
# while_count: вывод последовательности чисел
 
count=1
 
while [[ $count -le 5 ]]; do
 	echo $count
 	count=$((count + 1))
done
echo "Finished."

Если запустить этот сценарий, он выведет:

while_count
1
2
3
4
5
Finished

Команда while имеет следующий синтаксис:

while команды; do команды; done

Подобно if, команда while проверяет код завершения списка команд. Пока код завершения равен 0, она выполняет команды внутри цикла. В сценарии, приведенном выше, создается переменная count, и ей присваивается начальное значение 1. Команда while проверяет код завершения составной команды <html>циклы_while_и_until</html>. Пока <html>циклы_while_и_until</html> возвращает код 0, команды внутри цикла продолжают выполняться. В конце каждого цикла повторно выполняется команда <html>циклы_while_и_until</html>. После пяти итераций цикла значение переменной count увеличится до 6, команда <html>циклы_while_и_until</html> вернет код завершения, отличный от 0, и цикл завершится, а программа продолжит выполнение с инструкции, следующей непосредственно за циклом.

Цикл while можно использовать для усовершенствования программы read-menuиз предыдущей статьи:

#!/bin/bash
 
# while-menu: программа вывода системной информации,
# управляемая с помощью меню
 
DELAY=3 # Время отображения результатов на экране (в секундах)
 
while [[ "$REPLY" != 0 ]]; do
        clear
        cat <<- _EOF_
                Please select:
 
                1. Display system information
                2. Display disk space
                3. Display home space utilization
                0. Quit
 
        _EOF_
        read -p "Enter selection [0-3] > "
 
        if [[ "$REPLY" =~ ^[0-3]$ ]]; then
                if [[ "$REPLY" == 1 ]]; then
                        echo "Hostname: $HOSTNAME"
                        uptime
                        sleep "$DELAY"
                fi
                if [[ "$REPLY" == 2 ]]; then
                        df -h
                        sleep "$DELAY"
                fi
                if [[ "$REPLY" == 3 ]]; then
                        if [[ "$(id -u)" -eq 0 ]]; then
                                echo "Home space utilization (All users)"
                                du -sh /home/*
                        else
                                echo "Home space utilization ($USER)"
                                du -sh "$HOME"
                        fi
                        sleep "$DELAY"
                fi
        else
                echo "Invalid entry."
                sleep "$DELAY"
        fi
done
echo "Programm Terminated."

Заключив меню в цикл while, мы смогли заставить программу повторять вывод меню после каждой операции выбора. Цикл продолжает выполняться и выводить меню, пока переменная REPLY не получит значение 0, предоставляя пользователю возможность сделать другой выбор. После выполнения выбранной операции выполняется команда sleep, она приостанавливает программу на несколько секунд и дает возможность увидеть результаты до того, как экран будет очищен и на нем вновь появится меню. Когда переменная REPLY получит значение 0, соответствующее варианту «Quit» (выйти), цикл завершится и выполнение продолжится со строки, следующей за done.

Прерывание цикла

В bash имеются две встроенные команды для управления потоком выполнения внутри циклов. Команда break немедленно завершает цикл, после чего выполнение программы продолжается с первой инструкции, следующей за циклом. Команда пропускает оставшуюся часть цикла, и программа переходит к началу следующей итерации цикла. Ниже приводится версия программы while-menu, использующая обе команды – break и continue:

cat ~/bin/while_menu_2
#!/bin/bash
 
# while-menu: программа вывода системной информации,
# управляемая с помощью меню
 
DELAY=3 # Время отображения результатов на экране (в секундах)
 
while true; do
        clear
        cat <<- _EOF_
                Please select:
 
                1. Display system information
                2. Display disk space
                3. Display home space utilization
                0. Quit
 
        _EOF_
        read -p "Enter selection [0-3] > "
 
        if [[ "$REPLY" =~ ^[0-3]$ ]]; then
                if [[ "$REPLY" == 1 ]]; then
                        echo "Hostname: $HOSTNAME"
                        uptime
                        sleep "$DELAY"
                        continue
                fi
                if [[ "$REPLY" == 2 ]]; then
                        df -h
                        sleep "$DELAY"
                        continue
                fi
                if [[ "$REPLY" == 3 ]]; then
                        if [[ "$(id -u)" -eq 0 ]]; then
                                echo "Home space utilization (All users)"
                                du -sh /home/*
                        else
                                echo "Home space utilization ($USER)"
                                du -sh "$HOME"
                        fi
                        sleep "$DELAY"
                        continue
                fi
                if [[ "$REPLY" == 0 ]]; then
                        break
                fi
        else
                echo "Invalid entry."
                sleep "$DELAY"
        fi
done
echo "Programm Terminated."

В этой версии сценария используется бесконечный цикл (цикл, который никогда не завершится сам по себе), в котором команда while проверяет код завершения команды true. Так как true всегда возвращает код 0, цикл никогда не завершится. Этот прием на удивление широко используется в сценариях. Поскольку цикл никогда не завершится сам по себе, программист должен предусмотреть его принудительное прерывание в нужный момент времени. В этом сценарии выход из цикла осуществляется с помощью команды break, когда пользователь выберет пункт 0. В конец других операций добавлена команда continue, чтобы увеличить эффективность работы сценария. Встретив команду continue, сценарий перепрыгнет через остальной код в цикле, который не требуется выполнять для данного выбора. Например, если пользователь выбрал пункт 1, нет никаких причин проверять выбор остальных вариантов.

until

Команда until очень похожа на while, но завершает цикл не когда обнаружит ненулевой код завершения, а наоборот. Цикл until продолжается, пока не получит код завершения 0. В сценарии while-count цикл продолжает выполняться, пока значение переменной count меньше или равно 5. Тот же результат можно получить, переписав сценарий с командой until

#!/bin/bash
 
# until-count: вывод последовательности чисел
 
count=1
 
until [[ "$count" -gt 5 ]]; do
        echo "$count"
        count=$((count + 1))
done
echo "Finished."

С условным выражением $count -gt 5 команда until завершит цикл в нужный момент времени. Выбор между циклами while и until обычно зависит от того, в каком случае условное выражение будет более читабельным.

Чтение файлов в циклах

Команды while и until могут принимать данные со стандартного ввода. Это дает возможность обрабатывать файлы с их помощью. В следующем примере мы выведем содержимое файла <html>distros.txt</html>1), созданного в одной из предыдущих статей:

#!/bin/bash
 
# while-read: чтение строк из файла
 
while read distro version release; do
        printf "Distro: %s\tVersion: %s\tReleased: %s\n" \
        "$distro" \
        "$version" \
        "$release"
done < distros.txt

Чтобы перенаправить файл в цикл, мы поместили оператор перенаправления после инструкции done. Цикл будет вводить поля из указанного файла с помощью read. После ввода каждой строки команда read будет завершаться с кодом 0, пока не достигнет конца файла. В этот момент она вернет ненулевой код завершения, и цикл завершится. Цикл можно также использовать в конвейерах:

#!/bin/bash
 
# while-read2: чтение строк из файла
 
sort -k 1,1 -k 2n distros.txt | while read distro version release; do
        printf "Distro: %s\tVersion: %s\tReleased: %s\n" \
        "$distro" \
        "$version" \
        "$release"
done

Здесь вывод команды sort передается на стандартный ввод цикла, который выводит поток текста на экран. Но не забывайте, что конвейер выполняет цикл в подоболочке, поэтому после его завершения любые переменные, созданные в цикле, будут потеряны.

1)
файл с примерами
bash/циклы_while_и_until.txt · Last modified: 2023/04/06 10:18 (external edit)