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

#!/bin/bash

echo; echo "Нажмите клавишу и затем клавишу Return."
read Keypress

case "$Keypress" in
  [a-z]   ) echo "буква в нижнем регистре";;
  [A-Z]   ) echo "Буква в верхнем регистре";;
  [0-9]   ) echo "Цифра";;
  *       ) echo "Знак пунктуации, пробел или что-то другое";;
esac  # Допускается указыватль диапазоны символов в [квадратных скобках].

# Упражнение:
# --------
# Сейчас сценарий считывает нажатую клавишу и завершается.
# Измените его так, чтобы сценарий продолжал отвечать на нажатия клавиш,
# но завершался бы только после ввода символа "X".
# Подсказка: заключите все в цикл "while".

exit 0

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

#!/bin/bash

# Грубый пример базы данных

clear # Очистка экрана

echo "          Список"
echo "          ------"
echo "Выберите интересующую Вас персону:"
echo
echo "[E]vans, Roland"
echo "[J]ones, Mildred"
echo "[S]mith, Julie"
echo "[Z]ane, Morris"
echo

read person

case "$person" in
# Обратите внимание: переменная взята в кавычки.

  "E" | "e" )
  # Пользователь может ввести как заглавную, так и строчную букву.
  echo
  echo "Roland Evans"
  echo "4321 Floppy Dr."
  echo "Hardscrabble, CO 80753"
  echo "(303) 734-9874"
  echo "(303) 734-9892 fax"
  echo "revans@zzy.net"
  echo "Старый друг и партнер по бизнесу"
  ;;
# Обратите внимание: блок кода, анализирующий конкретный выбор, завершается
# двумя символами "точка-с-запятой".

  "J" | "j" )
  echo
  echo "Mildred Jones"
  echo "249 E. 7th St., Apt. 19"
  echo "New York, NY 10009"
  echo "(212) 533-2814"
  echo "(212) 533-9972 fax"
  echo "milliej@loisaida.com"
  echo "Подружка"
  echo "День рождения: 11 февраля"
  ;;

# Информация о Smith и Zane будет добавлена позднее.

          * )
   # Выбор по-умолчанию.
   # "Пустой" ввод тоже обрабатывается здесь.
   echo
   echo "Нет данных."
  ;;

esac

echo

#  Упражнение:
#  --------
#  Измените этот сценарий таким образом, чтобы он не завершал работу
#+ после вывода информации о персоне, а переходил на ожидание нового
#+ ввода от пользователя.

exit 0

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

#! /bin/bash

case "$1" in
"") echo "Порядок использования: ${0##*/} <filename>"; exit 65;;  # Параметры командной строки отсутствуют,
                                                  # или первый параметр -- "пустой".
# Обратите внимание на ${0##*/} это подстановка параметра ${var##pattern}. В результате получается $0.

-*) FILENAME=./$1;;   # Если имя файла (аргумент $1) начинается с "-",
                      # то заменить его на ./$1
                      # тогда параметр не будет восприниматься как ключ команды.

* ) FILENAME=$1;;     # В противном случае -- $1.
esac


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

#!/bin/bash
# Подстановка команд в "case".

case $( arch ) in # команда "arch" возвращает строку, описывающую аппаратную апхитектуру.
i386 ) echo "Машина на базе процессора 80386";;
i486 ) echo "Машина на базе процессора 80486";;
i586 ) echo "Машина на базе процессора Pentium";;
i686 ) echo "Машина на базе процессора Pentium2 или выше";;
*    ) echo "Машина на другом типе процессора";;
esac

exit 0

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

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

#!/bin/bash
# match-string.sh: простое сравнение строк

match_string ()
{
  MATCH=0
  NOMATCH=90
  PARAMS=2     # Функция требует два входных аргумента.
  BAD_PARAMS=91

  [ $# -eq $PARAMS ] || return $BAD_PARAMS

  case "$1" in
  "$2") return $MATCH;;
  *   ) return $NOMATCH;;
  esac

}


a=one
b=two
c=three
d=two


match_string $a     # неверное число аргументов
echo $?             # 91

match_string $a $b  # не равны
echo $?             # 90

match_string $b $d  # равны
echo $?             # 0


exit 0

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

#!/bin/bash
# isalpha.sh: Использование "case" для анализа строк.

SUCCESS=0
FAILURE=-1

isalpha ()  # Проверка - является ли первый символ строки символом алфавита.
{
if [ -z "$1" ]                # Вызов функции без входного аргумента?
then
  return $FAILURE
fi

case "$1" in
[a-zA-Z]*) return $SUCCESS;;  # Первый символ - буква?
*        ) return $FAILURE;;
esac
}             # Сравните с функцией "isalpha ()" в языке C.


isalpha2 ()   # Проверка - состоит ли вся строка только из символов алфавита.
{
  [ $# -eq 1 ] || return $FAILURE

  case $1 in
  *[!a-zA-Z]*|"") return $FAILURE;;
               *) return $SUCCESS;;
  esac
}

isdigit ()    # Проверка - состоит ли вся строка только из цифр.
{             # Другими словами - является ли строка целым числом.
  [ $# -eq 1 ] || return $FAILURE

  case $1 in
  *[!0-9]*|"") return $FAILURE;;
            *) return $SUCCESS;;
  esac
}



check_var ()  # Интерфейс к isalpha
{
if isalpha "$@"
then
  echo "\"$*\" начинается с алфавитного символа."
  if isalpha2 "$@"
  then        # Дальнейшая проверка не имеет смысла, если первй символ не буква.
    echo "\"$*\" содержит только алфавитные символы."
  else
    echo "\"$*\" содержит по меньшей мере один не алфавитный символ."
  fi
else
  echo "\"$*\" начинсется с не алфавитного символа ."
              #  Если функция вызвана без входного параметра,
              #+ то считается, что строка содержит "не алфавитной" символ.
fi

echo

}

digit_check ()  # Интерфейс к isdigit ().
{
if isdigit "$@"
then
  echo "\"$*\" содержит только цифры [0 - 9]."
else
  echo "\"$*\" содержит по меньшей мере один не цифровой символ."
fi

echo

}

a=23skidoo
b=H3llo
c=-What?
d=What?
e=`echo $b`   # Подстановка команды.
f=AbcDef
g=27234
h=27a34
i=27.34

check_var $a
check_var $b
check_var $c
check_var $d
check_var $e
check_var $f
check_var     # Вызов без параметра, что произойдет?
#
digit_check $g
digit_check $h
digit_check $i


exit 0        # Сценарий дополнен S.C.

# Упражнение:
# --------
#  Напишите функцию 'isfloat ()', которая проверяла бы вещественные числа.
#  Подсказка: Эта функция подобна функции 'isdigit ()',
#+ надо лишь добавить анализ наличия десятичной точки.
select

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

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



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

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

#!/bin/bash

PS3='Выберите ваш любимый овощ: ' # строка приглашения к вводу (prompt)

echo

select vegetable in "бобы" "морковь" "картофель" "лук" "брюква"
do
  echo
  echo "Вы предпочитаете $vegetable."
  echo ";-))"
  echo
  break  # если 'break' убрать, то получится бесконечный цикл.
done

exit 0

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

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

for variable [in list]

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

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

#!/bin/bash

PS3='Выберите ваш любимый овощ: '

echo

choice_of()
{
select vegetable
# список выбора [in list] отсутствует, поэтому 'select' использует входные аргументы функции.
do
  echo
  echo "Вы предпочитаете $vegetable."
  echo ";-))"
  echo
  break
done
}

choice_of бобы рис морковь редис томат шпинат
#         $1   $2  $3      $4    $5    $6
#         передача списка выбора в функцию choice_of()

exit 0

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