BashЯзык командного интерпретатора bash. Part 4. «Условные операторы»

Данный топик является четвертым топиком цикла «Язык командного интерпретатора bash». Он будет повествовать о таких управляющих структурах языка, как условные операторы. Но перед тем, как перейти к их описанию, необходимо остановиться на некоторых нюансах, которые сделают рассмотрение нижеизложенного материала более понятным.
Во-первых, разберём, что такое список команд. Список команд – это одиночная команда, конвейер или последовательность команд/конвейеров, разделённых одним из следующих операторов: ";", "&&", "||", завершённая точкой с запятой.
; — оператор последовательного выполнения нескольких команд. Каждая последующая команда начинает выполняться только после завершения предыдущей (неважно, успешного или нет);
&& — оператор выполнения команды только после успешного выполнения предыдущей;
|| — оператор выполнения команды только после ошибочного выполнения предыдущей.
Кодом успешного завершения является 0, а ошибочного — не ноль (зависит от типа ошибки). Не нужно путать с обычными языками программирования, когда 1 является аналогом true, а 0 – false.
Теперь можно приступить к непосредственному рассмотрению условных операторов.

Оператор вариантов case

Общий синтаксис оператора case:
case значение in
шаблон1) список1;;
шаблон2 | шаблон3) список2;;
esac

Логическая последовательность выполнения оператора case:
а) ищется первый шаблон, совпадающий со значением;
б) если он найден, выполняется соответствующий ему список команд, завершённый ";;";
в) управление передаётся операторам, следующим за конструкцией case.
Шаблон и список разделяются символом ")". Одному списку команд может соответствовать несколько условий, тогда их нужно разделять символом "|".
В шаблонах можно использовать символы "*", "?", "[ ]", о которых было рассказано во втором топике цикла. С их помощью можно реализовать инструкцию, действующую как default в операторе switch таких языков, как C, PHP.
Приведу пример использования case:
echo -n "[Универсальный просмоторщик] Укажите имя файла: "; read File
case "$File" in
*.jpg|*.gif|*.png)
eog $File
;;
*.pdf)       evince $File   ;;
*.txt)       less $File   ;;
*.html)      firefox $File   ;;
/dev/*)      echo "Ну это страшные файлы."   ;;
*)
echo "Ну ладно, ладно - не такой уж и универсальный."
echo "Этот тип файлов мне не знаком. Не знаю, чем его просмотреть."
;;
esac

Ещё один пример использования конструкции case:
echo "Ошибка. Кому отправить сообщение?"
echo "Начальнику: b"
echo "Коллегам: c"
echo "Никому: any key"
read answer
case $answer in
   b|B) mail –s "error log" boss < error.log;;
   c|C) mail –s "Help! error log" –c denis nick < error.log;;
   *) echo "error"; exit;;
esac


Условный оператор if

Общий синтаксис оператора if:
if список1 then
список2
[elif список3 then
список4]
[else
список5]
fi

Квадратные скобки здесь указывают на необязательные конструкции. Логическая последовательность выполнения оператора case:
а) выполняется список1;
б) если он выполнен без ошибок, выполняется список2. В противном случае выполняется список3, и если он завершается без ошибок – список4. Если же и список3 возвращает код ошибки, выполняется список5;
в) управление передаётся операторам, следующим за конструкцией if.
Приведу пример использования if:
if grep -q Bash file
then echo "Файл содержит, как минимум, одно слово Bash."
fi

Когда if и then располагаются в одной строке, то конструкции if и then должны завершаться точкой с запятой. Например:
$ if [ $? –ne 0 ]; then echo “Error”; fi

Теперь, зная о возможни располагать if и then в одной строке, перепишем вышеуказанный пример:
if grep -q Bash file; then 
echo «Файл содержит слово Bash.»
fi


Оператор test и условные выражения

В вышеприведённом примере вместо анализа кода завершения использована проверка условия. Две формы такой проверки эквивалентны: встроенная команда test и [условие]. Например, для проверки существования файла нужно написать:
test –e <файл>

или
[ -e <файл> ]

Если используются квадратные скобки, они обязательно должны быть отделены друг от друга пробелом, потому что "[" – это название команды, а "]" – это обязательный последний аргумент её завершения.
В случае успешной проверки условия, возвращается 0, а в случае ложности – код ошибки 1.
Команда test может проверять строку на пустоту. Непустая строка приводит к коду завершения 0. Пуста, соответственно – 1. Например:
$ test $USER; echo $?
0

Конструкция "[[ ]]" более универсальна, по сравнению с "[ ]". Этот расширенный вариант команды test. Внутри этой конструкции не производится никакой дополнительной интерпретации имен файлов и не производится разбиение аргументов на отдельные слова, но допускается подстановка параметров и команд. Например:
file=/etc/passwd
if [[ -e $file ]]
then
   echo “Файл паролей найден.”
fi

Конструкция "[[ ]]" более предпочтительна, нежели "[ ]", поскольку поможет избежать некоторых логических ошибок. Например, операторы "&&", "||", "<" и ">" внутри "[[ ]]" вполне допустимы, в то время как внутри "[ ]" порождают сообщения об ошибках.
Конструкция "(( ))" позволяет производить вычисление арифметических выражений внутри неё. Если результатом вычислений является ноль, то возвращается код ошибки. Ненулевой результат вычислений даёт код возврата 0. То есть полная противоположность инструкциям test и "[ ]", обсуждавшимся выше.
Оператор if позволяет допускать наличие вложенных проверок:
if echo "Следующий *if* находится внутри первого *if*."
   if [[ $comparison = "integer" ]]
   then (( a < b ))
   else
      [[ $a < $b ]]
   fi
then
   echo '$a меньше $b'
fi


Условные выражения можно комбинировать с помощью обычных логических операций:
! <выражение> – отрицание;
<выражение1> –a <выражение2> – логическое И;
<выражение1> –o <выражение2> – логическое ИЛИ.

Элементарные условные выражения для файлов:
-e — файл существует;
-f — обычный файл (не каталог и не файл устройства);
-s — ненулевой размер файла;
-d — файл является каталогом;
-b — файл является блочным устройством (floppy, cdrom и т.п.);
-c — файл является символьным устройством (клавиатура, модем, звуковая карта и т.п.);
-p — файл является каналом;
-h — файл является символической ссылкой;
-L — файл является символической ссылкой;
-S — файл является сокетом;
-t — файл связан с терминальным устройством;
-r — файл доступен для чтения (пользователю, запустившему сценарий);
-w — файл доступен для записи (пользователю, запустившему сценарий);
-x — файл доступен для исполнения (пользователю, запустившему сценарий);
-g — (sgid) флаг для файла или каталога установлен;
-u — (suid) флаг для файла установлен;
-k — флаг sticky bit установлен;
-O — вы являетесь владельцем файла;
-G — вы принадлежите к той же группе, что и файл;
-N — файл был модифицирован с момента последнего чтения;
файл1 -nt файл2 – файл1 более новый, чем файл2;
файл1 -ot файл2 – файл1 более старый, чем файл2;
файл1 -ef файл2 – файл1 и файл2 являются «жесткими» ссылками на один и тот же файл.

Элементарные условные выражение для сравнения строк:
-z строка – длина строки равна 0;
-n строка – длина строки не равно 0;
строка1 == строка2 – строки совпадают (аналог “=”);
строка1 !== строка2 – строки не совпадают (аналог “!=”);
строка1 < строка2 – строка1 предшествует строке2 в лексикографическом порядке;
строка1 > строка2 – строка1 следует за строкой2 в лексикографическом порядке.
Арифметическое условное выражение имеет формат:
аргумент1 операция аргумент2, где аргументами являются целые числа, и допустимы следующие операции:
-eq – равно;
-ne – не равно;
-lt – меньше;
-le – меньше или равно;
-gt – больше;
-ge – больше или равно;
< — меньше (внутри двойных круглых скобок);
<= — меньше или равно (внутри двойных круглых скобок);
> — больше (внутри двойных круглых скобок);
>= — больше или равно (внутри двойных круглых скобок).

Перепишем предыдущий пример с использованием оператора if:
echo "Ошибка. Кому отправить сообщение?"
echo "Начальнику: b"
echo "Коллегам: c"
echo "Никому: any key"
read answer
if [ "$answer" == "b" –o "$answer" == "B" ]; then
   mail –s "error log" boss < error.log;
elif [ "$answer" == "c" –o "$answer" == "C" ]; then
   mail –s "Help! error log" –c denis nick < error.log;
else
echo "error"; exit;
fi


В следующем топике я продолжу рассматривать управляющие структуры командного интерпретатора bash. А именно, будут рассмотрены операторы циклов. А сейчас жду комментариев и критики :).

UPD: Спасибо пользователю aonick за его замечания и критику.

Комментарии (9)

  • avatar
  • Dez
  • 10 июля 2009, 23:00
  • #
  • 3
отлично рассмотрены условные управляющие структуры и выражения.
Автор молодец, постарался. Спасибо. Ждем продолжения
Спасибо. Продолжение скоро будет.
Немного моего соуса в блюдо Istergul'а:
>> Кодом успешного завершения является 0, а ошибочного 1
О — код успешного завершения команды, а не ноль — код ошибки.
Наглядный пример:
# ls
reiting.html  script.sh
# echo $?
0
# ls /root/
ls: невозможно открыть каталог /root/: Отказано в доступе
# echo $?
2
# lsbebebetutut
bash: lsbebebetutut: команда не найдена
# echo $?
127

Видно разные значения кода ошибки($?).

Не знал, что коды ошибок бывают отличными от 1. Спасибо за данную информацию. Сейчас внесу соответствующие исправления.
На тон он и называется «код» то есть по этой цифре можно в документации посмотреть и понять какой ошибочной ситуации это соответствует.
Мой пример case:
echo -n "[Универсальный просмоторщик] Укажите имя файла: "; read File
case "$File" in
  *.jpg|*.gif|*.png)
       eog $File
  ;;
  *.pdf)       evince $File   ;;
  *.txt)       less $File   ;;
  *.html)      firefox $File
  /dev/*)      echo "Ну это страшные файлы."
  *)
      echo "Ну ладно, ладно - не такой уж и универсальный."
      echo "Этот тип файлов мне не знаком. Не знаю, чем его просмотреть."
  ;;
esac

два раза ;; забыл в конце строк: про html и про dev

я так понял свой коммент здесь отредактировать позже нельзя
Ещё раз огромное спасибо автору, как только закончится цикл статей повешу их в рамочку на сайте, обещаю ^^
Спасибо.
Думаю, общее число статей будет около 8-10.
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.