Сценарии командной оболочки. 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
>
>········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 в строке
Вторая функция, isLeapYear(), с помощью простых арифметических проверок выясняет, содержит ли февраль в указанном году 29-е число
В основном сценарии исходные данные передаются сценарию normdate, представленному выше, для нормализации
Запуск сценария
Запуская сценарий (как показано в листинге 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. Сможете ли вы написать двухстрочную функцию, проверяющую високосный год?