10.4. Операторы выбора


Инструкции case и select технически не являются циклами, поскольку не предусматривают многократное исполнение блока кода. Однако, они, как и циклы, управляют ходом исполнения программы, в зависимости от начальных или конечных условий.

case (in) / esac

Конструкция case эквивалентна конструкции switch в языке C/C++. Она позволяет выполнять тот или иной участок кода, в зависимости от результатов проверки условий. Она является, своего рода, краткой формой записи большого количества операторов if/then/else и может быть неплохим инструментом при создании разного рода меню.

case "$variable" in

 "$condition1" )
 command...
 ;;

 "$condition2" )
 command...
 ;;

esac



Note
  • Заключать переменные в кавычки необязательно, поскольку здесь не производится разбиения на отдельные слова.

  • Каждая строка с условием должна завершаться правой (закрывающей) круглой скобкой ).

  • Каждый блок команд, отрабатывающих по заданному условию, должен завершаться двумя символами точка-с-запятой ;;.

  • Блок case должен завершаться ключевым словом esac (case записанное в обратном порядке).



Пример 10-24. Использование case

  1. #!/bin/bash
  2. echo; echo "Нажмите клавишу и затем клавишу Return."
  3. read Keypress
  4. case "$Keypress" in
  5.   [a-z]   ) echo "буква в нижнем регистре";;
  6.   [A-Z]   ) echo "Буква в верхнем регистре";;
  7.   [0-9]   ) echo "Цифра";;
  8.   *       ) echo "Знак пунктуации, пробел или что-то другое";;
  9. esac  # Допускается указыватль диапазоны символов в [квадратных скобках].
  10. # Упражнение:
  11. # --------
  12. # Сейчас сценарий считывает нажатую клавишу и завершается.
  13. # Измените его так, чтобы сценарий продолжал отвечать на нажатия клавиш,
  14. # но завершался бы только после ввода символа "X".
  15. # Подсказка: заключите все в цикл "while".
  16. exit 0

Пример 10-25. Создание меню с помощью case

  1. #!/bin/bash
  2. # Грубый пример базы данных
  3. clear # Очистка экрана
  4. echo "          Список"
  5. echo "          ------"
  6. echo "Выберите интересующую Вас персону:"
  7. echo
  8. echo "[E]vans, Roland"
  9. echo "[J]ones, Mildred"
  10. echo "[S]mith, Julie"
  11. echo "[Z]ane, Morris"
  12. echo
  13. read person
  14. case "$person" in
  15. # Обратите внимание: переменная взята в кавычки.
  16.   "E" | "e" )
  17.   # Пользователь может ввести как заглавную, так и строчную букву.
  18.   echo
  19.   echo "Roland Evans"
  20.   echo "4321 Floppy Dr."
  21.   echo "Hardscrabble, CO 80753"
  22.   echo "(303) 734-9874"
  23.   echo "(303) 734-9892 fax"
  24.   echo "revans@zzy.net"
  25.   echo "Старый друг и партнер по бизнесу"
  26.   ;;
  27. # Обратите внимание: блок кода, анализирующий конкретный выбор, завершается
  28. # двумя символами "точка-с-запятой".
  29.   "J" | "j" )
  30.   echo
  31.   echo "Mildred Jones"
  32.   echo "249 E. 7th St., Apt. 19"
  33.   echo "New York, NY 10009"
  34.   echo "(212) 533-2814"
  35.   echo "(212) 533-9972 fax"
  36.   echo "milliej@loisaida.com"
  37.   echo "Подружка"
  38.   echo "День рождения: 11 февраля"
  39.   ;;
  40. # Информация о Smith и Zane будет добавлена позднее.
  41.           * )
  42.    # Выбор по-умолчанию.
  43.    # "Пустой" ввод тоже обрабатывается здесь.
  44.    echo
  45.    echo "Нет данных."
  46.   ;;
  47. esac
  48. echo
  49. #  Упражнение:
  50. #  --------
  51. #  Измените этот сценарий таким образом, чтобы он не завершал работу
  52. #+ после вывода информации о персоне, а переходил на ожидание нового
  53. #+ ввода от пользователя.
  54. exit 0

Очень хороший пример использования case для анализа аргументов, переданных из командной строки.

  1. #! /bin/bash
  2. case "$1" in
  3. "") echo "Порядок использования: ${0##*/} <filename>"; exit 65;;  # Параметры командной строки отсутствуют,
  4.                                                   # или первый параметр — "пустой".
  5. # Обратите внимание на ${0##*/} это подстановка параметра ${var##pattern}. В результате получается $0.
  6. -*) FILENAME=./$1;;   # Если имя файла (аргумент $1) начинается с "-",
  7.                       # то заменить его на ./$1
  8.                       # тогда параметр не будет восприниматься как ключ команды.
  9. * ) FILENAME=$1;;     # В противном случае — $1.
  10. esac


Пример 10-26. Оператор case допускает использовать подстановку команд вместо анализируемой переменной

  1. #!/bin/bash
  2. # Подстановка команд в "case".
  3. case $( arch ) in # команда "arch" возвращает строку, описывающую аппаратную апхитектуру.
  4. i386 ) echo "Машина на базе процессора 80386";;
  5. i486 ) echo "Машина на базе процессора 80486";;
  6. i586 ) echo "Машина на базе процессора Pentium";;
  7. i686 ) echo "Машина на базе процессора Pentium2 или выше";;
  8. *    ) echo "Машина на другом типе процессора";;
  9. esac
  10. exit 0

Оператор case допускает использование шаблонных конструкций.

Пример 10-27. Простой пример сравнения строк

  1. #!/bin/bash
  2. # match-string.sh: простое сравнение строк
  3. match_string ()
  4. {
  5.   MATCH=0
  6.   NOMATCH=90
  7.   PARAMS=2     # Функция требует два входных аргумента.
  8.   BAD_PARAMS=91
  9.   [ $# -eq $PARAMS ] || return $BAD_PARAMS
  10.   case "$1" in
  11.   "$2") return $MATCH;;
  12.   *   ) return $NOMATCH;;
  13.   esac
  14. }
  15. a=one
  16. b=two
  17. c=three
  18. d=two
  19. match_string $a     # неверное число аргументов
  20. echo $?             # 91
  21. match_string $a $b  # не равны
  22. echo $?             # 90
  23. match_string $b $d  # равны
  24. echo $?             # 0
  25. exit 0

Пример 10-28. Проверка ввода

  1. #!/bin/bash
  2. # isalpha.sh: Использование "case" для анализа строк.
  3. SUCCESS=0
  4. FAILURE=-1
  5. isalpha ()  # Проверка - является ли первый символ строки символом алфавита.
  6. {
  7. if [ -z "$1" ]                # Вызов функции без входного аргумента?
  8. then
  9.   return $FAILURE
  10. fi
  11. case "$1" in
  12. [a-zA-Z]*) return $SUCCESS;;  # Первый символ - буква?
  13. *        ) return $FAILURE;;
  14. esac
  15. }             # Сравните с функцией "isalpha ()" в языке C.
  16. isalpha2 ()   # Проверка - состоит ли вся строка только из символов алфавита.
  17. {
  18.   [ $# -eq 1 ] || return $FAILURE
  19.   case $1 in
  20.   *[!a-zA-Z]*|"") return $FAILURE;;
  21.                *) return $SUCCESS;;
  22.   esac
  23. }
  24. isdigit ()    # Проверка - состоит ли вся строка только из цифр.
  25. {             # Другими словами - является ли строка целым числом.
  26.   [ $# -eq 1 ] || return $FAILURE
  27.   case $1 in
  28.   *[!0-9]*|"") return $FAILURE;;
  29.             *) return $SUCCESS;;
  30.   esac
  31. }
  32. check_var ()  # Интерфейс к isalpha
  33. {
  34. if isalpha "$@"
  35. then
  36.   echo "\"$*\" начинается с алфавитного символа."
  37.   if isalpha2 "$@"
  38.   then        # Дальнейшая проверка не имеет смысла, если первй символ не буква.
  39.     echo "\"$*\" содержит только алфавитные символы."
  40.   else
  41.     echo "\"$*\" содержит по меньшей мере один не алфавитный символ."
  42.   fi
  43. else
  44.   echo "\"$*\" начинсется с не алфавитного символа ."
  45.               #  Если функция вызвана без входного параметра,
  46.               #+ то считается, что строка содержит "не алфавитной" символ.
  47. fi
  48. echo
  49. }
  50. digit_check ()  # Интерфейс к isdigit ().
  51. {
  52. if isdigit "$@"
  53. then
  54.   echo "\"$*\" содержит только цифры [0 - 9]."
  55. else
  56.   echo "\"$*\" содержит по меньшей мере один не цифровой символ."
  57. fi
  58. echo
  59. }
  60. a=23skidoo
  61. b=H3llo
  62. c=-What?
  63. d=What?
  64. e=`echo $b`   # Подстановка команды.
  65. f=AbcDef
  66. g=27234
  67. h=27a34
  68. i=27.34
  69. check_var $a
  70. check_var $b
  71. check_var $c
  72. check_var $d
  73. check_var $e
  74. check_var $f
  75. check_var     # Вызов без параметра, что произойдет?
  76. #
  77. digit_check $g
  78. digit_check $h
  79. digit_check $i
  80. exit 0        # Сценарий дополнен S.C.
  81. # Упражнение:
  82. # --------
  83. #  Напишите функцию 'isfloat ()', которая проверяла бы вещественные числа.
  84. #  Подсказка: Эта функция подобна функции 'isdigit ()',
  85. #+ надо лишь добавить анализ наличия десятичной точки.
select

Оператор select был заимствован из Korn Shell, и является еще одним инструментом, используемым при создании меню.

select variable [in list]
do
 command...
 break
done



Этот оператор предлагает пользователю выбрать один из представленных вариантов. Примечательно, что select по-умолчанию использует в качестве приглашения к вводу (prompt) — PS3 (#? ), который легко изменить.

Пример 10-29. Создание меню с помощью select

  1. #!/bin/bash
  2. PS3='Выберите ваш любимый овощ: ' # строка приглашения к вводу (prompt)
  3. echo
  4. select vegetable in "бобы" "морковь" "картофель" "лук" "брюква"
  5. do
  6.   echo
  7.   echo "Вы предпочитаете $vegetable."
  8.   echo ";-))"
  9.   echo
  10.   break  # если 'break' убрать, то получится бесконечный цикл.
  11. done
  12. exit 0

Если в операторе select список in list не задан, то в качестве списка будет использоваться список аргументов ($@), передаваемый сценарию или функции.

Сравните это с поведением оператора цикла

for variable [in list]

в котором не задан список аргументов.

Пример 10-30. Создание меню с помощью select в функции

  1. #!/bin/bash
  2. PS3='Выберите ваш любимый овощ: '
  3. echo
  4. choice_of()
  5. {
  6. select vegetable
  7. # список выбора [in list] отсутствует, поэтому 'select' использует входные аргументы функции.
  8. do
  9.   echo
  10.   echo "Вы предпочитаете $vegetable."
  11.   echo ";-))"
  12.   echo
  13.   break
  14. done
  15. }
  16. choice_of бобы рис морковь редис томат шпинат
  17. #         $1   $2  $3      $4    $5    $6
  18. #         передача списка выбора в функцию choice_of()
  19. exit 0

См. так же Пример 34-3.