7.1. Конструкции проверки условий


  • Оператор if/then проверяет — является ли код завершения списка команд 0 (поскольку 0 означает "успех"), и если это так, то выполняет одну, или более, команд, следующие за словом then.

  • Существует специальная команда — [ (левая квадратная скобка). Она является синонимом команды test, и является встроенной командой (т.е. более эффективной, в смысле производительности). Эта команда воспринимает свои аргументы как выражение сравнения или как файловую проверку и возвращает код завершения в соответствии с результатами проверки (0 — истина, 1 — ложь).

  • Начиная с версии 2.02, Bash предоставляет в распоряжение программиста конструкцию [[ ... ]] расширенный вариант команды test, которая выполняет сравнение способом более знакомым программистам, пишущим на других языках программирования. Обратите внимание: [[ — это зарезервированное слово, а не команда.

    Bash исполняет [[ $a -lt $b ]] как один элемент, который имеет код возврата.

    Круглые скобки (( ... )) и предложение let ... так же возвращают код 0, если результатом арифметического выражения является ненулевое значение. Таким образом, арифметические выражения могут учавствовать в операциях сравнения.

    1. Предложение let "1<2" возвращает 0 (так как результат сравнения "1<2""1", или "истина")
    2. (( 0 && 1 )) возвращает 1 (так как результат операции "0 && 1""0", или "ложь")


  • Условный оператор if проверяет код завершения любой команды, а не только результат выражения, заключенного в квадратные скобки.

    1. if cmp a b &> /dev/null  # Подавление вывода.
    2. then echo "Файлы a и b идентичны."
    3. else echo "Файлы a и b имеют различия."
    4. fi
    5. if grep -q Bash file
    6. then echo "Файл содержит, как минимум, одно слово Bash."
    7. fi
    8. word=Linux
    9. letter_sequence=inu
    10. if echo "$word" | grep -q "$letter_sequence"
    11. # Подавление вывода ключом "-q" в команде grep.
    12. then
    13.   echo "Последовательность $letter_sequence обнаружена в слове $word"
    14. else
    15.   echo "Последовательность $letter_sequence, в слове $word не найдена"
    16. fi
    17. if COMMAND_WHOSE_EXIT_STATUS_IS_0_UNLESS_ERROR_OCCURRED
    18. then echo "Команда выполнена успешно."
    19. else echo "Обнаружена ошибка при выполнении команды."
    20. fi


  • Оператор if/then допускает наличие вложенных проверок.

    1. if echo "Следующий *if* находится внутри первого *if*."
    2.   if [[ $comparison = "integer" ]]
    3.     then (( a < b ))
    4.   else
    5.     [[ $a < $b ]]
    6.   fi
    7. then
    8.   echo '$a меньше $b'
    9. fi


    Это детальное описание конструкции "if-test" любезно предоставлено Stephane Chazelas.

Пример 7-1. Что есть "истина"?

  1. #!/bin/bash
  2. echo
  3. echo "Проверяется \"0\""
  4. if [ 0 ]      # ноль
  5. then
  6.   echo "0 — это истина."
  7. else
  8.   echo "0 — это ложь."
  9. fi            # 0 — это истина.
  10. echo
  11. echo "Проверяется \"1\""
  12. if [ 1 ]      # единица
  13. then
  14.   echo "1 — это истина."
  15. else
  16.   echo "1 — это ложь."
  17. fi            # 1 — это ложь.
  18. echo
  19. echo "Testing \"-1\""
  20. if [ -1 ]     # минус один
  21. then
  22.   echo "-1 — это истина."
  23. else
  24.   echo "-1 — это ложь."
  25. fi            # -1 — это истина.
  26. echo
  27. echo "Проверяется \"NULL\""
  28. if [ ]        # NULL (пустое условие)
  29. then
  30.   echo "NULL — это истина."
  31. else
  32.   echo "NULL — это ложь."
  33. fi            # NULL — это ложь.
  34. echo
  35. echo "Проверяется \"xyz\""
  36. if [ xyz ]    # строка
  37. then
  38.   echo "Случайная строка — это истина."
  39. else
  40.   echo "Случайная строка — это ложь."
  41. fi            # Случайная строка — это истина.
  42. echo
  43. echo "Проверяется \"\$xyz\""
  44. if [ $xyz ]   # Проверка, если $xyz это null, но...
  45.               # только для неинициализированных переменных.
  46. then
  47.   echo "Неинициализированная переменная — это истина."
  48. else
  49.   echo "Неинициализированная переменная — это ложь."
  50. fi            # Неинициализированная переменная — это ложь.
  51. echo
  52. echo "Проверяется \"-n \$xyz\""
  53. if [ -n "$xyz" ]            # Более корректный вариант.
  54. then
  55.   echo "Неинициализированная переменная — это истина."
  56. else
  57.   echo "Неинициализированная переменная — это ложь."
  58. fi            # Неинициализированная переменная — это ложь.
  59. echo
  60. xyz=          # Инициализирована пустым значением.
  61. echo "Проверяется \"-n \$xyz\""
  62. if [ -n "$xyz" ]
  63. then
  64.   echo "Пустая переменная — это истина."
  65. else
  66.   echo "Пустая переменная — это ложь."
  67. fi            # Пустая переменная — это ложь.
  68. echo
  69. # Кргда "ложь" истинна?
  70. echo "Проверяется \"false\""
  71. if [ "false" ]              #  это обычная строка "false".
  72. then
  73.   echo "\"false\" — это истина." #+ и она истинна.
  74. else
  75.   echo "\"false\" — это ложь."
  76. fi            # "false" — это истина.
  77. echo
  78. echo "Проверяется \"\$false\""  # Опять неинициализированная переменная.
  79. if [ "$false" ]
  80. then
  81.   echo "\"\$false\" — это истина."
  82. else
  83.   echo "\"\$false\" — это ложь."
  84. fi            # "$false" — это ложь.
  85.               # Теперь мв получили ожидаемый результат.
  86. echo
  87. exit 0

Упражнение. Объясните результаты, полученные в Пример 7-1.

  1. if [ condition-true ]
  2. then
  3.    command 1
  4.    command 2
  5.    ...
  6. else
  7.    # Необязательная ветка (можно опустить, если в ней нет необходимости).
  8.    # Дополнительный блок кода,
  9.    # исполняемый в случае, когда результат проверки — "ложь".
  10.    command 3
  11.    command 4
  12.    ...
  13. fi


Note

Когда if и then располагаются в одной строке, то конструкция if должна завершаться точкой с запятой. И if, и then — это зарезервированные слова. Зарезервированные слова начинают инструкцию, которая должна быть завершена прежде, чем в той же строке появится новая инструкция.

  1. if [ -x "$filename" ]; then


Else if и elif

elif

elif — это краткая форма записи конструкции else if. Применяется для построения многоярусных инструкций if/then.

  1. if [ condition1 ]
  2. then
  3.    command1
  4.    command2
  5.    command3
  6. elif [ condition2 ]
  7. # То же самое, что и else if
  8. then
  9.    command4
  10.    command5
  11. else
  12.    default-command
  13. fi


Конструкция if test condition-true является точным эквивалентом конструкции if [ condition-true ], где левая квадратная скобка [ выполняет те же действия, что и команда test. Закрывающая правая квадратная скобка ] не является абсолютно необходимой, однако, более новые версии Bash требуют ее наличие.

Note

Команда test — это встроенная команда Bash, которая выполняет проверки файлов и производит сравнение строк. Таким образом, в Bash-скриптах, команда test не вызывает внешнюю (/usr/bin/test) утилиту, которая является частью пакета sh-utils. Аналогично, [ не производит вызов утилиты /usr/bin/[, которая является символической ссылкой на /usr/bin/test.

  1. bash$ type test
  2. test is a shell builtin
  3. bash$ type '['
  4. [ is a shell builtin
  5. bash$ type '[['
  6. [[ is a shell keyword
  7. bash$ type ']]'
  8. ]] is a shell keyword
  9. bash$ type ']'
  10. bash: type: ]: not found
  11.        


Пример 7-2. Эквиваленты команды test/usr/bin/test, [ ], и /usr/bin/[

  1. #!/bin/bash
  2. echo
  3. if test -z "$1"
  4. then
  5.   echo "Аргументы командной строки отсутствуют."
  6. else
  7.   echo "Первый аргумент командной строки: $1."
  8. fi
  9. echo
  10. if /usr/bin/test -z "$1"      # Дает тот же рузультат, что и встроенная команда "test".
  11. then
  12.   echo "Аргументы командной строки отсутствуют."
  13. else
  14.   echo "Первый аргумент командной строки: $1."
  15. fi
  16. echo
  17. if [ -z "$1" ]                # Функционально идентично вышеприведенному блоку кода.
  18. #   if [ -z "$1"                эта конструкция должна работать, но...
  19. #+  Bash выдает сообщение об отсутствующей закрывающей скобке.
  20. then
  21.   echo "Аргументы командной строки отсутствуют."
  22. else
  23.   echo "Первый аргумент командной строки: $1."
  24. fi
  25. echo
  26. if /usr/bin/[ -z "$1"         # Функционально идентично вышеприведенному блоку кода.
  27. # if /usr/bin/[ -z "$1" ]     # Работает, но выдает сообщение об ошибке.
  28. then
  29.   echo "Аргументы командной строки отсутствуют."
  30. else
  31.   echo "Первый аргумент командной строки: $1."
  32. fi
  33. echo
  34. exit 0

Конструкция [[ ]] более универсальна, по сравнению с [ ]. Этот расширенный вариант команды test перекочевал в Bash из ksh88.

Note

Внутри этой конструкции не производится никакой дополнительной интерпретации имен файлов и не производится разбиение аргументов на отдельные слова, но допускается подстановка параметров и команд.

  1. file=/etc/passwd
  2. if [[ -e $file ]]
  3. then
  4.   echo "Файл паролей найден."
  5. fi


Tip

Конструкция [[ ... ]] более предпочтительна, нежели [ ... ], поскольку поможет избежать некоторых логических ошибок. Например, операторы &&, ||, < и > внутри [[ ]] вполне допустимы, в то время как внутри [ ] порождают сообщения об ошибках.

Note

Строго говоря, после оператора if, ни команда test, ни квадратные скобки ( [ ] или [[ ]] ) не являются обязательными.

  1. dir=/home/bozo
  2. if cd "$dir" 2>/dev/null; then   # "2>/dev/null" подавление вывода сообщений об ошибках.
  3.   echo "Переход в каталог $dir выполнен."
  4. else
  5.   echo "Невозможно перейти в каталог $dir."
  6. fi
Инструкция "if COMMAND" возвращает код возврата команды COMMAND.

Точно так же, условие, находящееся внутри квадратных скобок может быть проверено без использования оператора if.

  1. var1=20
  2. var2=22
  3. [ "$var1" -ne "$var2" ] && echo "$var1 не равно $var2"
  4. home=/home/bozo
  5. [ -d "$home" ] || echo "каталог $home не найден."


Внутри (( )) производится вычисление арифметического выражения. Если результатом вычислений является ноль, то возвращается 1, или "ложь". Ненулевой результат дает код возврата 0, или "истина". То есть полная противоположность инструкциям test и [ ], обсуждавшимся выше.

Пример 7-3. Арифметические выражения внутри (( ))

  1. #!/bin/bash
  2. # Проверка арифметических выражений.
  3. # Инструкция (( ... )) вычисляет арифметические выражения.
  4. # Код возврата противоположен коду возврата инструкции [ ... ] !
  5. (( 0 ))
  6. echo "Код возврата \"(( 0 ))\":  $?."         # 1
  7. (( 1 ))
  8. echo "Код возврата \"(( 1 ))\":  $?."         # 0
  9. (( 5 > 4 ))                                   # true
  10. echo "Код возврата \"(( 5 > 4 ))\":  $?."     # 0
  11. (( 5 > 9 ))                                   # false
  12. echo "Код возврата \"(( 5 > 9 ))\":  $?."     # 1
  13. (( 5 - 5 ))                                   # 0
  14. echo "Код возврата \"(( 5 - 5 ))\":  $?."     # 1
  15. (( 5 / 4 ))                                   # Деление, все в порядке
  16. echo "Код возврата \"(( 5 / 4 ))\":  $?."     # 0
  17. (( 1 / 2 ))                                   # Результат деления < 1.
  18. echo "Код возврата \"(( 1 / 2 ))\":  $?."     # Округляется до 0.
  19.                                               # 1
  20. (( 1 / 0 )) 2>/dev/null                       # Деление на 0.
  21. echo "Код возврата \"(( 1 / 0 ))\":  $?."     # 1
  22. # Для чего нужна инструкция "2>/dev/null" ?
  23. # Что произойдет, если ее убрать?
  24. # Попробуйте убрать ее и выполнить сценарий.
  25. exit 0