Ковыряясь в скриптах наткнулся на интересный case:
...
case "$what" in
web)
cmd="$web pub '$int'"
m=100
u='%'
break;;
api)
cmd="$web api '$int'"
m=100
u='%'
break;;
...
Чё тут интересного-то? Подсказка, это не цикл. Да, вот эти вот break'и выглядят тут совершенно инородно. Возможно когда-то этот кусок эм, кода работал в цикле и break каким-то боком был нужен? Но сейчас цикла нет а brake есть. Бомбит? Бомбит до такой степени что я решился писнуть небольшую статейку про case. На 100%-ю полноту освещения не претендую, все мои эксперименты лишь очень поверхностные но несколько точек постараюсь расставить.
Что за case такой? Ну это когда вы пишите очередной эйай на bash'е и делаете if ... then ... elif ... then ... else ... fi
и получается простыня из иф-елсов. Так вот не надо так. Case делает это гораздо проще и элегантней. Посмотрим простой пример, допустим нам надо выполнить ряд различных действий в зависимости от значения какой-то переменной. Как это выглядит в иф-елсе:
#!/bin/bash
var=$1
if [[ $var == 'бле' ]]; then
echo $var'ск'
elif [[ $var == 'бло' ]]; then
echo $var'ха ха-ха'
elif [[ $var == 'бла' ]]; then
echo $var'нк'
elif [[ $var == 'бля' ]]; then
echo 'огло'$var
else
echo 'хз'
fi
И чем сложнее ваш эйай тем уё... сложнее разгребать получившуюся иф-елсевую лапшу. А бывает внутри иф-елсов пихают еще иф-елсы и еще... В итоге получается действительно (с)ложный эйай. В котором потом далеко не каждый кожаный ай сможет разобраться. Давайте посмотрим чем на это ответит case
:
#!/bin/bash
var=$1
case "$var" in
бле) echo $var'ск' ;;
бло) echo $var'ха ха-ха';;
бла) echo $var'нк' ;;
бля) echo 'огло'$var ;;
*) echo 'хз' ;;
esac
Oh-la-la! Выглядит очень секси!) Познакомимся поближе с этим замечательным case'ом. Что мы видим? Вначале собственно оператор case
затем искомая переменная $var
и ключевое слово in
, как в циклах for i in ...
, не это ли причина брейка? А после in'а перечисляются собственно кейсы, in this case, in that case...
Кейс выглядит как проверяемое значение (бле, бло ...) отделенное закрывающей скобкой, строго говоря проверяемое значение должно быть заключено в скобки, т.е. должно быть так:
case "$var" in
(бле) echo $var'ск' ;;
(бло) echo $var'ха ха-ха';;
...
Но открывающую скобку разрешается опускать, что все и делают. Далее идет пространство команд которые будут выполнены в случае совпадения.
Заканчивается кейс двойной точкой с запятой ;;
. Вы можете написать сколь угодно большой блок команд, соблюдая синтаксис bash'а, но заканчиваться блок должен двойной точкой с запятой ;;
. Т.е. возможны вот такие варианты:
case "$var" in
бле) echo $var'ск';;
бло) echo $var'ха ха-ха'; foo=bar;;
бла) echo $var'нк'
alice=boobs
nick=dick
;;
...
И т.д. насколько фантазия позволяет. И никаких брейков тут не надо, не будь брейккером, заканчивается кейс двойной точкой с запятой ;;
. Повторение - мать учения. Кейсы проверяются в том порядке в котором они указаны, если один из кейсов совпал, обработка остальных кейсов прекращается. Соответственно порядок имеет значение, если по какой-то причине проверка на 'бло' важнее проверки на 'бле' то стоит поднять 'бло' кейс выше 'бле' кейса.
Да, можно делать case
в case
но выглядит это еще хуже и запутаней иф-елса, постарайтесь этого избегать.
А что такое *
? Case поддерживает регулярные выражения. Набор их ограничен но они есть. Символ *
- это один из спец. символов регулярок кейса, он означает любое значение либо ничто.
В завершении ключевое слово esac
. Это конец, всё, все кейсы закончились.
Для тех кто в танке, esac
это case
наоборот, так же как if ... fi
и прочий апож.
Пробежимся по регулярным выражениям case
, символ *
как уже говорилось, означает любое значение. Сам по себе применяется для обработки ошибок, неподдерживаемых параметров и значений по умолчанию. Либо в составе более сложных выражений. Пример ошибки/значения по умолчанию у нас уже есть:
...
*) echo 'хз';;
...
Ответом на любое значение переменной $var
отличное от 'бл*' будет 'хз'. И вот уже готово выражение для обработки всех 'бл*' кейсов:
var=$1
case "$var" in
бл*) echo $var ;;
*) echo 'хз' ;;
esac
Проверим что получилось:
$ ./test бла
бла
$ ./test бл
бл
$ ./test б
хз
Любой символ либо ничто. Но что если ничто нас не устраивает? Нужно что-то. Что? Вопрос, он же ответ:
#!/bin/bash
var=$1
case "$var" in
бл?) echo $var ;;
*) echo 'хз' ;;
esac
Символ ?
в кейсе означает любой символ (но не ничто), пробуем:
$ ./test бл
хз
$ ./test блы
блы
$ ./test блыы
хз
Теперь работает как надо, обрабатывается значение не более трех символов, два первых символа 'бл'. Попробуем ограничить охват, создадим кейс для обработки только 'бло' и 'бля' одной командой. Для этого используются квадратные скобки []
#!/bin/bash
var=$1
case "$var" in
бл[оя]) echo ${var}ха ;;
*) echo 'хз' ;;
esac
Смотрим что получилось:
$ ./test б
хз
$ ./test бло
блоха
$ ./test бля
бляха
$ ./test блы
хз
Но это еще не всё! Можно объединить несколько кейсов с помощью символа |
, он работает как 'или', выглядит это как-то так:
#!/bin/bash
var=$1
case "$var" in
бл[оя]|кака|руба|Петру) echo ${var}ха ;;
*) echo 'хз' ;;
esac
тестим:
$ ./test бля
бляха
$ ./test кака
какаха
$ ./test петру
хз
$ ./test Петру
Петруха
Обратили внимание что Петруха сломался в первой попытке? Потому что caseсенсетив. Поправим это:
#!/bin/bash
var=$1
case "$var" in
бл[оя]|кака|руба|[Пп]етру) echo ${var}ха ;;
*) echo 'хз' ;;
esac
Готово, Петруха в порядке:
$ ./test петру
петруха
$ ./test Петру
Петруха
Или так, если хочется весь case сделать нечувствительным к регистру:
#!/bin/bash
var=$1
case "${var,,}" in
бл[оя]|кака|руба|петру) echo ${var}ха ;;
*) echo 'хз' ;;
esac
Смотрим:
$ ./test Петру
Петруха
$ ./test петру
петруха
Этот чит код не евляется частью case
, это стандартная обработка переменых, но в case
это бывает очень удобно использовать. Есть такие варианты:
var=ЁКлМн
echo ${var,} # вывести первый символ в нижнем регистре
ёКлМн
echo ${var,,} # вывести все символы в нижнем регистре
ёклмн
echo ${var^} # вывести первый символ в верхнем регистре
ЁКлМн
echo ${var^^} # вывести все символы в верхнем регистре
ЁКЛМН
Объединение позволяет городить довольно сложные кейсы. Но очевидным минусом слияния кейсов через |
является увеличение кейса. Хотя... Может увеличение кейса и не минус вовсе?)
Я уже говорил что заканчивается кейс двойной точкой с запятой ;;
? Да, это уже 4-й раз. Так вот, это не так...
О_о
Вернее да, это так, заканчивается кейс двойной точкой с запятой ;;
(5-й). Но есть еще два варианта: ;&
и ;;&
. Эти окончания меняют поведение case
следующим образом:
;&
- если кейс совпал, выполнить код кейса и код следующего кейса (без проверки), пример:
#!/bin/bash
var=$1
case "$var" in
бла) echo $var'нк';&
'') echo $var'го';;
бля) echo $var'ха';;
*) echo 'хз' ;;
esac
$ ./test бла
бланк
благо
$ ./test бля
бляха
$ ./test
го
Код второго кейса выполнился несмотря на то что он совсем не совпадает с шаблоном. Последовательность кейсов важна.
;;&
- если кейс совпал, выполнить код кейса и продолжить проверку остальных кейсов, пример:
#!/bin/bash
var=$1
case "$var" in
бла) echo $var'нк';;&
'') echo $var'го';;
бля) echo $var'ха';;
*) echo 'хз' ;;
esac
$ ./test бла
бланк
хз
Совпал первый кейс, продолжил проверку и совпал последний кейс, т.к. *
срабатывает на любое значение, даже пустое.
Эти два метода могут служить альтернативой объединения через |
, но тут тоже можно нагородить такой эйай что с ходу не разберешься.
Смотрите по ситуации, что удобней и понятней читается в той или иной ситуации, то и используйте.
Вот такой вот интересный этот case
. Еще больше интересных case'ов можно подсмотреть в моих поделках: piu-piu, sshto, kui и др...
Это моя первая среднеразмерная писулька в этом году, так что всех с Новым!)
Удачи всем нам и интересных case'ов!)
Творите, выдумывайте, пробуйте!)