Составные списки командного интерпретатора Linux: ветвление

Условные списки «И» и «ИЛИ» являются простейшей формой ветвления хода выполнения сценария в зависимости от успеха или неудачи выполнения той или иной команды.

При помощи специальной команды 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 ~$

 

Добавить комментарий