Дейв Тейлор - Сценарии командной оболочки. Linux, OS X и Unix. 2-е издание
- Название:Сценарии командной оболочки. Linux, OS X и Unix. 2-е издание
- Автор:
- Жанр:
- Издательство:Питер
- Год:2017
- Город:СПб.
- ISBN:978-5-496-03029-8
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Дейв Тейлор - Сценарии командной оболочки. Linux, OS X и Unix. 2-е издание краткое содержание
Цель этой книги — продемонстрировать практические приемы программирования сценариев на bash и познакомить с самыми распространенными утилитами на коротких и компактных примерах, не вдаваясь в излишние подробности. Экспериментируйте с этими сценариями — ломайте, исправляйте и приспосабливайте их под свои нужды, чтобы понять, как они работают. Только так вы сможете решать самые сложные задачи.
Сценарии командной оболочки. Linux, OS X и Unix. 2-е издание - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
··#### ДНЕЙ В НАЧАЛЬНОМ ГОДУ
··# Собрать строку формата с начальной датой.
··startdatefmt="$startmon/$startday/$startyear"
··calculate="$((10#$($date −d "12/31/$startyear" +%j))) \
····-$((10#$($date −d $startdatefmt +%j)))"
··daysleftinyear=$(($calculate))
··#### ДНЕЙ В ПРОМЕЖУТОЧНЫХ ГОДАХ
··daysbetweenyears=0
··tempyear=$(($startyear + 1))
··while [$tempyear −lt $thisyear]; do
····daysbetweenyears=$(($daysbetweenyears + \
····$((10#$($date −d "12/31/$tempyear" +%j)))))
····tempyear=$(($tempyear + 1))
··done
··#### ДНЕЙ В ТЕКУЩЕМ ГОДУ
··dayofyear=$($date +%j) # Это просто!
··#### ТЕПЕРЬ СЛОЖИТЬ ВСЕ ВМЕСТЕ
··totaldays=$(($((10#$daysleftinyear)) + \
····$((10#$daysbetweenyears)) + \
····$((10#$dayofyear))))
··/bin/echo −n "$totaldays days have elapsed between "
··/bin/echo −n "$startmon/$startday/$startyear "
··echo "and today, day $dayofyear of $thisyear."
··exit 0
Как это работает
Сценарий получился довольно длинным, но не слишком сложным. Функция определения високосного года достаточно простая — она всего лишь проверяет, равно ли количество дней в году 366.
В строке выполняется интересная проверка наличия GNU-версии date в системе перед продолжением работы.
Оператор перенаправления отбрасывает любой вывод и сообщения об ошибках, а возвращаемый код проверяется на неравенство нулю, которое свидетельствует об ошибке при попытке разобрать параметр −version. В OS X, например, устанавливается версия date с минимальными возможностями — она не поддерживает параметра −version и многих других.
Далее остается только выполнить простейшие арифметические операции. Спецификатор %j возвращает номер дня в году, поэтому вычисление дней, оставшихся до конца года от начальной даты выполняется просто: . Суммарное количество дней в промежуточных годах вычисляется в цикле while, в котором очередной год хранится в переменной tempyear.
Наконец, определение числа дней от начала текущего года до текущей даты реализуется проще простого: .
dayofyear=$($date +%j)
Затем остается только сложить полученные дни и вывести результат!
Запуск сценария
Вернемся вновь в листинге 15.4 к историческим датам, рассматривавшимся выше.
Листинг 15.4.Запуск сценария daysago для разных дат
$ daysago 7 20 1969
17106 days have elapsed between 7/20/1969 and today, day 141 of 2016.
$ daysago 6 6 1944
26281 days have elapsed between 6/6/1944 and today, day 141 of 2016.
$ daysago 1 1 2010
2331 days have elapsed between 1/1/2010 and today, day 141 of 2016.
Эти команды были выполнены… Пусть date скажет за нас:
$ date
Fri May 20 13:30:49 UTC 2016
Усовершенствование сценария
Имеется одно ошибочное условие, которое не проверяется сценарием: когда прошлая дата находится лишь в нескольких днях от текущей или даже в будущем. Что получится в таких ситуациях и как исправить эту ошибку? (Совет: загляните в сценарий № 101, где вы найдете дополнительные проверки, которые можно применить в этом сценарии.)
№ 101. Вычисление дней до указанной даты
Логичным дополнением сценария № 100, daysago, является другой сценарий, daysuntil. Он, по сути, выполняет те же вычисления, но сначала подсчитывает количество дней, оставшихся до конца текущего года, затем количество дней в промежуточных годах и количество дней до указанной даты в целевом году, как показано в листинге 15.5.
Код
Листинг 15.5.Сценарий daysuntil
··#!/bin/bash
··# daysuntil — фактически выполняет те же вычисления, что и daysago,
··#·· но в обратном порядке, когда текущая дата становится "датой в прошлом",
··#·· а дата в будущем — "текущей".
··# Так же, как в предыдущем сценарии, используйте 'which gdate’ в OS X
··#·· и 'which date' в Linux.
··date="$(which gdate)"
··function daysInMonth
··{
····case $1 in
······1|3|5|7|8|10|12) dim=31;; # Постоянное значение
······4|6|9|11········) dim=30;;
······2··············) dim=29;; # Зависит от года: високосный/невисокосный
······*··············) dim=-1;; # Неизвестный месяц
····esac
··}
··function isleap
··{
····# Возвращает ненулевое значение в $leapyear, если $1 — високосный год.
····leapyear=$($date −d 12/31/$1 +%j | grep 366)
··}
··#######################
··#### ОСНОВНОЙ БЛОК
··#######################
··if [$# −ne 3]; then
····echo "Usage: $(basename $0) mon day year"
····echo " with just numerical values (ex: 1 1 2020)"
····exit 1
··fi
··$date −version > /dev/null 2>&1 # Отбросить сообщение об ошибке, если появится.
··if [$? -ne 0]; then
····echo "Sorry, but $(basename $0) can't run without GNU date." >&2
····exit 1
··fi
··eval $($date "+thismon=%m;thisday=%d;thisyear=%Y;dayofyear=%j")
··endmon=$1; endday=$2; endyear=$3
··# Необходимые проверки параметров…
··daysInMonth $endmon # Инициализирует переменную $dim
··if [$endday −lt 0 −o $endday −gt $dim]; then
····echo "Invalid: Month #$endmon only has $dim days." >&2
····exit 1
··fi
··if [$endmon −eq 2 −a $endday −eq 29]; then
····isleap $endyear
····if [-z "$leapyear"]; then
······echo "Invalid: $endyear wasn't a leapyear; February had 28 days." >&2
······exit 1
····fi
··fi
··if [$endyear −lt $thisyear]; then
····echo "Invalid: $endmon/$endday/$endyear is prior to the current year." >&2
····exit 1
··fi
··if [$endyear −eq $thisyear −a $endmon −lt $thismon]; then
····echo "Invalid: $endmon/$endday/$endyear is prior to the current month." >&2
····exit 1
··fi
··if [$endyear −eq $thisyear −a $endmon −eq $thismon −a $endday −lt $thisday]
··then
····echo "Invalid: $endmon/$endday/$endyear is prior to the current date." >&2
····exit 1
··fi
··if [$endyear −eq $thisyear −a $endmon −eq $thismon −a $end −e— eq $thisday]
··then
····echo "There are zero days between $endmon/$endday/$endyear and today." >&2
····exit 0
··fi
··#### Если целевая дата находится в этом же году,
··####·· вычисления должны выполняться немного иначе.
··if [$endyear −eq $thisyear]; then
····totaldays=$(($($date −d "$endmon/$endday/$endyear" +%j) −$($date +%j)))
··else
····#### Вычислить количество дней по фрагментам,
····####·· начиная с количества дней до конца текущего года.
····#### ДНЕЙ ОСТАЛОСЬ В НАЧАЛЬНОМ ГОДУ
····# Собрать строку формата с начальной датой.
····thisdatefmt="$thismon/$thisday/$thisyear"
····calculate="$($date −d "12/31/$thisyear" +%j) −$($date −d $thisdatefmt +%j)"
····daysleftinyear=$(($calculate))
Интервал:
Закладка: