Ковыряясь в скриптах наткнулся на интересный 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 'хз'          ;;
esacOh-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'ов!)
Творите, выдумывайте, пробуйте!)
Комментарии (8)
 - CaptainFlint10.01.2025 09:08- Так а break-то там в итоге зачем?  - vaniacer Автор10.01.2025 09:08- И никаких брейков тут не надо, не будь брейккером, заканчивается кейс двойной точкой с запятой - ;; - CaptainFlint10.01.2025 09:08- Это понятно, что в норме он там не нужен — потому и возник вопрос, откуда всё-таки в коде взялся break и для чего его туда засунули. Конечно, есть вариант, что писал сишник, привыкший ставить брейки, но ведь могли быть и другие причины… 
 
 
 - AnthonyAxenov10.01.2025 09:08- Все привыкли к башу как к данности, так что стоит лишний раз явно упомянуть: - bash!=- sh, и- shпросто не поддерживает ни кейс, ни многое другое башовое. Это так, просто дружеское напоминание тем, кто может споткнуться. - oldnomad10.01.2025 09:08- shпросто не поддерживает ни кейс, ни многое другое башовое.- Эээ, вообще-то как раз поддерживает: 2.9.4.3 Case Conditional Construct. Башизмами в данной статье являются: - Нестандартные расширения переменных, не перечисленные в 2.6.2 Parameter Expansion. 
- Нестандартное окончание ветки - ;;&. POSIX допускает только- ;;и- ;&, причём второй вариант довольно новый, и много кем не поддерживается; например,- dash(- shпо умолчанию в Debian) знает только- ;;.
 - А сам - caseпоявился еще в Bourne Shell в Unix V7 (1979). - AnthonyAxenov10.01.2025 09:08- Спасибо. Да, в деталях я профан, но всё же моё напоминание не лишено смысла. Например, недавно писал скрипт под - bash, который поломался под- ash-ем в alpine 3.21, пришлось коректировать. Фокус в том, что там- /bin/shэто симлинк на- ashв busybox (технически, нам сам- /bin/busybox) v1.37.
 
 
 
           
 
mafia8
Хабр не показывает в статье то, что идёт до ката.
vaniacer Автор
Поправил, спасибо.