Сценарии командной оболочки. Linux, OS X и Unix. 2-е издание - страница 19

стр.

>

>··fi

>··# Разбить нормализованную дату, в которой

>··#·· первое слово = месяц, второе слово = число месяца

>··#·· третье слово = год.

>··month="$(echo $newdate | cut −d\ −f1)"

>··day="$(echo $newdate | cut −d\ −f2)"

>··year="$(echo $newdate | cut −d\ −f3)"

>··# После нормализации данных проверить допустимость

>··#·· числа месяца (например, Jan 36 является недопустимой датой).

>··if! exceedsDaysInMonth $month "$2"; then

>····if ["$month" = "Feb" −a "$2" −eq "29"]; then

>······if! isLeapYear $3; then

>········echo "$0: $3 is not a leap year, so Feb doesn't have 29 days." >&2

>········exit 1

>······fi

>····else

>······echo "$0: bad day value: $month doesn't have $2 days." >&2

>······exit 1

>····fi

>··fi

>··echo "Valid date: $newdate"

>··exit 0

Как это работает

Этот сценарий было очень интересно писать, потому что он требует проверки большого количества непростых условий: числа месяца, високосного года и так далее. Логика сценария не просто проверяет месяц как число от 1 до 12 или день — от 1 до 31. Чтобы сценарий проще было писать и читать, в нем используются специализированные функции.

Первая функция, exceedsDaysInMonth(), анализирует месяц, указанный пользователем, разрешая вероятные допущения (например, пользователь может передать название JANUAR, и оно будет правильно опознано). Анализ выполняется инструкцией case в строке

, которая преобразует свой аргумент в нижний регистр и затем сравнивает полученное значение с константами, чтобы получить число дней в месяце. Единственный недостаток — для февраля функция всегда возвращает 28 дней.

Вторая функция, isLeapYear(), с помощью простых арифметических проверок выясняет, содержит ли февраль в указанном году 29-е число

.

В основном сценарии исходные данные передаются сценарию normdate, представленному выше, для нормализации

и затем разбиваются на три поля: $month, $day и $year. Затем вызывается функция exceedsDaysInMonth для проверки допустимости указанного числа для данного месяца, при этом 29 февраля обрабатывается отдельно — в этом случае вызовом функции isLeapYear проверяется год
и при необходимости выводится сообщение об ошибке. Если пользовательская дата успешно преодолела все проверки, значит, она допустимая!

Запуск сценария

Запуская сценарий (как показано в листинге 1.15), введите в командной строке дату в формате месяц-день-год. Месяц можно указать в виде трехсимвольного сокращения, полного названия или числа; год должен состоять из четырех цифр.

Результаты

Листинг 1.15. Тестирование сценария valid-date

>$ valid-date august 3 1960

>Valid date: Aug 3 1960

>$ valid-date 9 31 2001

>valid-date: bad day value: Sep doesn’t have 31 days.

>$ valid-date feb 29 2004

>Valid date: Feb 29 2004

>$ valid-date feb 29 2014

>valid-date: 2014 is not a leap year, so Feb doesn’t have 29 days.

Усовершенствование сценария

Подход, аналогичный используемому в этом сценарии, можно применить для проверки значения времени в 24-часовом формате или в 12-часовом формате с суффиксом AM/PM (Ante Meridiem/Post Meridiem — пополуночи/пополудни). Разбив значение времени по двоеточиям, нужно убедиться, что число минут и секунд (если указано) находится в диапазоне от 0 до 59, и затем проверить первое поле на вхождение в диапазон от 0 до 12, если присутствует суффикс AM/PM, или от 0 до 24, если предполагается 24-часовой формат. К счастью, несмотря на существование секунд координации (високосных секунд) и других небольших корректировок, помогающих сохранить сбалансированность календарного времени, их можно игнорировать в повседневной работе, то есть нет необходимости использовать замысловатые вычисления.

При наличии доступа к GNU-команде date в Unix или GNU/Linux можно использовать совершенно иной способ проверки високосных лет. Попробуйте выполнить следующую команду и посмотрите, что получится:

>$ date −d 12/31/1996 +%j

Если у вас в системе используется новейшая, улучшенная версия date, вы получите результат 366. Более старая версия просто пожалуется на ошибочный формат входных данных. Теперь подумайте о результате, возвращаемом новейшей командой date. Сможете ли вы написать двухстрочную функцию, проверяющую високосный год?