Ковыряясь в скриптах наткнулся на интересный 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’ов!)
Творите, выдумывайте, пробуйте!)
ссылка на оригинал статьи https://habr.com/ru/articles/872608/
Добавить комментарий