Поиск по сайту:

Как использовать операторы case в сценариях Bash


Операторы case в Bash эффективны, но при этом просты в написании. Когда вы вернетесь к старому скрипту Linux, вы будете рады, что использовали оператор case вместо длинного оператора if-then-else.

Заявление о деле

Большинство языков программирования имеют свою версию оператора switch или case. Они направляют поток выполнения программы в соответствии со значением переменной. Как правило, существует ветвь выполнения, определенная для каждого из ожидаемых возможных значений переменной, и одна универсальная или по умолчанию ветвь для всех остальных значений.

Логическая функциональность аналогична длинной последовательности операторов if-then с оператором else, перехватывающим все, что ранее не обрабатывалось одним из if операторы.

Реализация case в Bash пытается сопоставить выражение с одним из предложений. Он делает это, по очереди просматривая каждое предложение, пытаясь найти соответствующий шаблон. Шаблоны в предложениях — это строки, но, как это ни парадоксально, это не означает, что мы не можем использовать числовые значения в качестве выражения.

Общий случай

Общая форма оператора case такова:

case expression in 

  pattern-1)
    statement 
    ;;

  pattern-2) 
    statement
    ;;
    .
    .
    .

  pattern-N) 
    statement 
    ;;

  *) 
    statement 
    ;; 
esac

  • Инструкция case должна начинаться с ключевого слова case и заканчиваться ключевым словом esac.
  • Выражение оценивается и сравнивается с шаблонами в каждом предложении до тех пор, пока не будет найдено совпадение.
  • Выполняется оператор или операторы в соответствующем предложении.
  • Двойная точка с запятой «;;» используется для завершения предложения.
  • Если шаблон найден и операторы в этом предложении выполнены, все остальные шаблоны игнорируются.
  • Количество предложений не ограничено.
  • Звездочка «*» обозначает шаблон по умолчанию. Если выражение не соответствует ни одному из других шаблонов в операторе case, выполняется предложение по умолчанию.

Простой пример

Этот скрипт сообщает нам часы работы воображаемого магазина. Он использует команду date со строкой формата +\%a\ для получения сокращенного названия дня. Он хранится в переменной DayName.

#!/bin/bash

DayName=$(date +"%a")

echo "Opening hours for $DayName"

case $DayName in

  Mon)
    echo "09:00 - 17:30"
    ;;

  Tue)
    echo "09:00 - 17:30"
    ;;

  Wed)
    echo "09:00 - 12:30"
    ;;

  Thu)
    echo "09:00 - 17:30"
    ;;

  Fri)
    echo "09:00 - 16:00"
    ;;

  Sat)
    echo "09:30 - 16:00"
    ;;

  Sun)
    echo "Closed all day"
    ;;

  *)
    ;;
esac

Скопируйте этот текст в редактор и сохраните его как файл с именем «open.sh».

Нам нужно будет использовать команду chmod, чтобы сделать его исполняемым. Вам нужно будет сделать это для всех сценариев, которые вы создадите по мере работы с этой статьей.

chmod +x open.sh

Теперь мы можем запустить наш скрипт.

./open.sh

День, когда был сделан снимок экрана, приходится на пятницу. Это означает, что переменная DayName содержит строку «Пт». Это соответствует образцу «Пт» в предложении «Пт».

Обратите внимание, что шаблоны в предложениях не нужно заключать в двойные кавычки, но это не повредит, если они будут. Однако вы должны использовать двойные кавычки, если шаблон содержит пробелы.

Предложение по умолчанию оставлено пустым. Все, что не соответствует ни одному из предыдущих предложений, игнорируется.

Этот сценарий работает, и его легко читать, но он многословен и повторяется. Мы можем довольно легко сократить этот тип оператора case.

Использование нескольких шаблонов в предложении

Очень удобная функция операторов case заключается в том, что вы можете использовать несколько шаблонов в каждом предложении. Если выражение соответствует любому из этих шаблонов, операторы в этом предложении выполняются.

Вот скрипт, который говорит вам, сколько дней в месяце. Ответов может быть только три: 30 дней, 31 день или 28 или 29 дней для февраля. Итак, хотя есть 12 месяцев, нам нужно только три пункта.

В этом скрипте пользователю предлагается ввести название месяца. Чтобы сделать сопоставление с образцом нечувствительным к регистру, мы используем команду shop с параметром -s nocasematch. Не имеет значения, содержит ли ввод прописные, строчные буквы или их смесь.

#!/bin/bash

shopt -s nocasematch

echo "Enter name of a month"
read month

case $month in

  February)
    echo "28/29 days in $month"
    ;;

  April | June | September | November)
    echo "30 days in $month"
    ;;

  January | March | May | July | August | October | December)
    echo "31 days in $month"
    ;;

  *)
    echo "Unknown month: $month"
    ;;
esac

Февраль получает отдельный пункт, а все остальные месяцы делят два пункта в зависимости от того, 30 или 31 день в них. В предложениях с несколькими шаблонами используется символ вертикальной черты «|». как разделитель. Случай по умолчанию улавливает неправильно написанные месяцы.

Мы сохранили это в файл под названием «month.sh» и сделали его исполняемым.

chmod +x month.sh

Мы запустим скрипт несколько раз и покажем, что не имеет значения, используем ли мы верхний или нижний регистр.

./month.sh

Поскольку мы сказали сценарию игнорировать различия в верхнем и нижнем регистре, любое правильно написанное название месяца обрабатывается одним из трех основных предложений. Плохо написанные месяцы перехватываются предложением по умолчанию.

Использование цифр в операторах case

Мы также можем использовать цифры или числовые переменные в качестве выражения. Этот сценарий просит пользователя ввести число в диапазоне от 1 до 3. Чтобы было понятно, что шаблоны в каждом предложении являются строками, они заключены в двойные кавычки. Несмотря на это, сценарий по-прежнему сопоставляет ввод пользователя с соответствующим предложением.

#!/bin/bash

echo "Enter 1, 2, or 3: "
read Number

case $Number in

  "1")
    echo "Clause 1 matched"
    ;;

  "2")
    echo "Clause 2 matched"
    ;;

  "3")
    echo "Clause 3 matched"
    ;;

  *)
    echo "Default clause matched"
    ;;
esac

Сохраните это в файл с именем «number.sh», сделайте его исполняемым, а затем запустите:

./number.sh

Использование операторов case в циклах for

Оператор case пытается сопоставить шаблону одно выражение. Если вам нужно обработать много выражений, вы можете поместить оператор case в цикл for.

Этот сценарий выполняет команду ls для получения списка файлов. В цикле for подстановка файлов, похожая на регулярные выражения, но отличающаяся от нее, применяется к каждому файлу по очереди для извлечения расширения файла. Это хранится в строковой переменной Extension.

Оператор case использует переменную Extension в качестве выражения, которое пытается сопоставить с предложением.

#!/bin/bash

for File in $(ls)

do
  # extract the file extension
  Extension=${File##*.}

  case "$Extension" in

    sh)
      echo " Shell script: $File"
      ;;

    md)
      echo " Markdown file: $File"
      ;;

    png)
      echo "PNG image file: $File"
      ;;

    *)
      echo "Unknown: $File"
      ;;
  esac
done

Сохраните этот текст в файл с именем «filetype.sh», сделайте его исполняемым, а затем запустите его, используя:

./filetype.sh

Наш минималистичный скрипт идентификации типа файла работает.

Обработка кодов выхода с операторами case

Программа с хорошим поведением отправит код выхода в оболочку, когда она завершится. Традиционная схема использует нулевое значение кода выхода, чтобы указать на безпроблемное выполнение, и значения, равные одному или нескольким, чтобы указать различные типы ошибок.

Многие программы используют только ноль и единицу. Объединение всех условий ошибки в один код выхода усложняет выявление проблем, но это обычная практика.

Мы создали небольшую программу под названием «go-geek», которая случайным образом возвращала коды выхода, равные нулю или единице. Следующий скрипт вызывает go-geek. Он получает код выхода с помощью переменной оболочки $? и использует ее как выражение для инструкции case.

Реальный сценарий будет выполнять соответствующую обработку в зависимости от успеха или неудачи команды, сгенерировавшей код выхода.

#!/bin/bash

go-geek

case $? in

  "0")
    echo "Response was: Success"
    echo "Do appropriate processing in here"
    ;;

  "1")
    echo "Response was: Error"
    echo "Do appropriate error handling in here"
    ;;

  *)
    echo "Unrecognised response: $?"
    ;;
esac

Сохраните это в скрипт под названием «return-code.sh» и сделайте его исполняемым. Вам нужно будет заменить нашу команду go-geek другой командой. Вы можете попробовать cd в каталог, который не существует, чтобы получить код выхода из единицы, а затем отредактировать свой скрипт на cd в доступном каталоге, чтобы получить выход код нуля.

Запуск скрипта несколько раз показывает, что различные коды выхода правильно идентифицируются оператором case.

./return-code.sh

Разборчивость помогает ремонтопригодности

Возвращаться к старым сценариям Bash и выяснять, как они делают то, что делают, особенно если они были написаны кем-то другим, непросто. Изменение функциональности старых скриптов еще сложнее.

Оператор case дает вам логику ветвления с ясным и простым синтаксисом. Это беспроигрышный вариант.