Интересный case

от автора

Ковыряясь в скриптах наткнулся на интересный 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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *