Как стать автором
Обновить

Интересный case

Уровень сложностиПростой
Время на прочтение5 мин
Количество просмотров655

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

Творите, выдумывайте, пробуйте!)

Теги:
Хабы:
Если эта публикация вас вдохновила и вы хотите поддержать автора — не стесняйтесь нажать на кнопку
+3
Комментарии2
3

Публикации

Истории

Ближайшие события