Данный топик является четвертым топиком цикла «Язык командного интерпретатора 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 за его замечания и критику.
Немного моего соуса в блюдо Istergul'а:
>> Кодом успешного завершения является 0, а ошибочного 1
О — код успешного завершения команды, а не ноль — код ошибки.
Наглядный пример:
# ls
reiting.html script.sh
# echo $?
0
# ls /root/
ls: невозможно открыть каталог /root/: Отказано в доступе
# echo $?
2
# lsbebebetutut
bash: lsbebebetutut: команда не найдена
# echo $?
127
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
Автор молодец, постарался. Спасибо. Ждем продолжения
>> Кодом успешного завершения является 0, а ошибочного 1
О — код успешного завершения команды, а не ноль — код ошибки.
Наглядный пример:
Видно разные значения кода ошибки($?).
я так понял свой коммент здесь отредактировать позже нельзя
Думаю, общее число статей будет около 8-10.