Условные списки «И» и «ИЛИ» являются простейшей формой ветвления хода выполнения сценария в зависимости от успеха или неудачи выполнения той или иной команды.
При помощи специальной команды test, позволяющей выполнять проверки логических выражений, можно осуществлять ветвление сценария, например, в зависимости от определенных условий или значений тех или иных параметров. Аналогично специальной команде expr, предназначенной для вычисления арифметических выражений.
В листинге ниже показано, что команда test и ее «красивая» форма [ изначально являются внешними командами, что так же влечет за собой накладные расходы на системные вызовы fork и execve. Поэтому в большинстве интерпретаторов обе формы команды test реализованы еще и как встроенные команды.
Содержимое
Команды test и [
bender@ubuntu:~$ which test
/usr/bin/test
bender@ubuntu:~$ which [
/usr/bin/[
bender@ubuntu:~$ type -a test
test встроена в оболочку t
est является /usr/bin/test
bender@ubuntu:~$ type -a [
[ встроена в оболочку [ является /usr/bin/[
bender@ubuntu:~$ test -f /etc/passwd
bender@ubuntu:~$ echo $?
0
bender@ubuntu:~$ [ -w /etc/passwd
-bash: [: пропущен ‘]’
bender@ubuntu:~$ [ -w /etc/passwd ]
bender@ubuntu:~$ echo $?
1
Команда test выполняет проверку логических выражений и заканчивается успехом, если проверяемое выражение истинно, и неуспехом, если проверяемое выражение ложно.
Именно то ее свойство используется для реализации ветвления. Например, во втором примере из листинга ниже проверяется наличие блочного (-b) файла устройства /dev/cdrom и при наличии запускается команда eject, предписывающая драйверу устройства открыть лоток природа CD/DVD.
Ветвление при помощи условных списков
bender@ubuntu:~$ which clear && clear|| tput clear
bender@ubuntu:~$ [ -b /dev/cdrom ] && eject /dev/cdrom
Условные списки удобно использовать для ветвления, если в каждой ветви выполняется по одной команде, как в первом примере из листинга выше, где посредством which проверяется наличие команды clear, которая при наличии и вызывается для очистки терминала. В противном случае, терминал очищается при помощи управляющей последовательности, которую выводит команда tput.
При необходимости выполнить в каждой ветви сценария несколько команд, используется конструкции ветвления «ЕСЛИ» вида
if [!] list; then list; [elif [!] list; then list;] … [else list;] fi
являющаяся составным списком и использующая ключевые слова языка командного интерпретатора: if, then, elif, else, fi.
Стоит заметить, что в самой конструкции логические выражения отсутствуют, а ветвление основано на статусах завершения списков команд, указывающихся после ключевых слов if и elif.
В листинге ниже из ISO-образа извлекается каталог sys/src, но двумя разными способами. Если установлен архиватор 7z и обнаружен командой which, то выполняется «успешная ветвь» с его использованием, иначе выполняется «неуспешная» ветвь с использованием монтирования/размонтирования ISO-образа при помощи Fuseiso.
Простое ветвление по результату выполнения команд
bender@ubuntu:~$ if which 7z ↵
> then ↵
> 7z x plan9.iso sys/src ↵
> else ↵
> fuseiso dvd.iso ~/dvd ↵
> cp -a ~/dvd/sys/src . ↵
> fuse mount -u ~/dvd ↵
> fi ↵
bender@ubuntu:~$ if ! findmnt ~/dvd ↵
> then ↵
> fuseiso dvd.iso ~/dvd↵
>fi↵
bender@ubuntu:~$ if test -b /dev/cdrom; then eject /dev/cdrom; fi
В примере из листинга выше используется признак отрицания !, который заставляет if действовать «наоборот», т. е. запускать ветвь then, если список после if закончился неуспешно, и ветвь else в противном случае. В нижнем примере стоит обратить внимание на то, что списки, использующиеся в конструкции if, могут отделяться символом ;, а не символом перевода строки ↵, как в верхних примерах.
В листинге ниже приведен маленький сценарий на языке командного интерпретатора, исполняющийся интерактивно в режиме трассировки команд.
В нем используются практически все конструкции командного интерпретатора: перенаправление потоков, конвейер, подстановки команд, параметров, арифметических выражений, экранирование и составной список ветвления.
Простое ветвление по результату проверки условий
bender@ubuntu:~$ set -x
bender@ubuntu:~$ load=$(awk ‘{printf «%d», $1*100 + $2*10 + $3}’ < /proc/loadavg)
++ awk ‘{printf «Xd», $1*100 + $2*10 + $3}’
+ load=86
bender@ubuntu:~$ power=$(( $(nproc)*100 ))
++ nproc + power=400
bender@ubuntu:~$ temp=$(( $(cat /sys/class/thermal/thermal_zone0/temp)/1000 ))
++ cat /sys/class/thermal/thermal_zone0/temp
+ temp=55
bender@ubuntu:~$ if [ $load -ge $power -a $temp -gt 80 ] ↵
> then↵
> notify-send «Обнаружена перегрузка: t=$temp C» «$(top -bn1 | head -5)» ↵
> fi ↵
+ ‘[‘ 86 -ge 400 -a 55 -gt 80′]’.
Сначала вычисляется «интегральная» нагрузка на систему load путем «взвешивания» с весами 100, 10 и 1 статистики за последние 1, 5 и 15 мин о среднем количестве процессов, стоящих в очереди на получение процессорного времени или доступа к устройству ввода-вывода.
Измеряемая ядром статистика считывается из трех столбцов одной строки файла /proc/loadavg псевдофайловой системы proc. Взвешивание выполняется при помощи процессора текстовых таблиц W:[AWK], основное назначение которого состоит в разбиении строк на столбцы и построчном выполнении указанных действий над ними.
В данном случае awk выводит в формате (printf) целого числа, (%d) результат взвешивания на основе значений статистики из первого ($1), второго ($2) и третьего ($3) столбцов.
Затем вычисляется «мощность» вычислительной системы power путем умножения 100 (процентов) на количество ядер процессора.
Следующим шагом вычисляется температура temp процессора в градусах Цельсия (*С), рассчитываемая путем деления нацело измеряемой ядром (из псевдофайловой системы sysfs) температуры на 1000 (миллиградусов).
Завершает сценарий составной список ветвления if, проверяющий при помощи «красивой» формы [ команды test логическое выражение нагрузка ≥ мощность И температура > 80°С на истинность и уведомляющий при помощи nodtify-send пользователя о перегрузке, в сообщении которого будет приведена текущая статистика потребления ресурсов из первых пяти строк вывода команды top.
Еще дин вид составного списка, предназначенного для организации множественного ветвления, реализуется конструкцией
case word in [ [(] pattern1 [| pattern2]… ) list1 ;;.]… esac
с ключевыми словами case, in, esac и признаком окончания ветви ;;. При множественном ветвлении используются не логические, а шаблонные выражения, заимствованные у подстановок имен файлов. При «обычном» ветвлении if-then-else для определения ветви исполнения тоже используются не логические выражения, а статусы завершения списков.
Для определения ветви исполнения слово word проверяют на соответствие шаблонам patterni слева направо (сверху вниз), при совпадении с одним из которых выполняется соответствующий список команд listj, и дальше никакие проверки не производятся.
В примере из листинга ниже сильно заэкранированный текст небольшого сценария командного интерпретатора присваивается переменной prompt_command, выполнение команд которой происходит каждый раз перед выводом первичного приглашения PS1.
В роли такой команды выступает составной список множественного ветвления case, который анализирует статус завершения последней выполненной интерпретатором команды при помощи подстановки значения специального параметра $?.
Если статус завершения равен 0 (успех) или 127 (команда не найдена), то вспомогательной «выдуманной» переменной PROMPT_COLOR присваивается управляющая последовательность (см. статью управляющие последовательности Linux) «нормального» (sgrO) режима вывода на терминал.
Если статус завершения равен 130 (штатное завершение по сигналу SIGINT, например, при нажатии на ^С) или 131 (аварийное завершение по сигналу SIGQUIT, например, при нажатии на ^\), то используется управляющая последовательность «негативного» (rev) вывода. Если же команда завершилась со статусом 1 или с любым другим ошибочным статусом, то будет использована управляющая последовательность «жирного» (bold) начертания.
Множественное ветвление
bender@ubuntu:~$ PROMPT.COHHAND=↵
> case $? in ↵
> 0|127) PROMPT_COLOR=’tput sgrO’ ;; ↵
> 13[01]) PROMPT_COLOR= ‘tput rev’ ;; ↵
> 1) PROMPT.COLOR-‘tput bold’ ;; ↵
> *) echo «exit cod = $?»; PROMPT_COLOR=’tput bold ↵
> esac’↵
bender@ubuntu:~$ PS1=’ echo $PROMPT_COLOR\u@\h \w\$ ‘tput sgrO’’
bender@ubuntu:~$ dd
^С0+0 записей получено
0+0 записей отправлено
скопировано 0 байт (0 В), 0,900673 с, 0,0 кВ/с
bender@ubuntu:~$ date
Пн. янв. 4 15:39:18 MSK 2018 MSK 2018
bender@ubuntu ~$ grep bender /etc/shadow
grep: /etc/shadow: Отказано в доступе
exit code = 2 в
bender@ubuntu ~$