Расписание последнего дня каждого месяца


10

Я прочитал инструкцию, чтобы запланировать сценарий на последний день месяца:

Примечание
. Проницательному читателю может быть интересно, как вы можете настроить команду для выполнения в последний день каждого месяца, потому что вы не можете установить значение dayofmonth для покрытия каждого месяца. Эта проблема преследует программистов Linux и Unix и породила немало разных решений. Распространенным методом является добавление оператора if-then, который использует команду date, чтобы проверить, является ли завтрашняя дата 01:

00 12 * * * if [`date +%d -d tomorrow` = 01 ] ; then ; command1

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

введите описание изображения здесь

Как [`date +%d -d tomorrow` = 01 ]работает?
Это правильно заявить then; command1?


Вы уверены, что это дословно, что это говорит? Как написано здесь, это на самом деле не работает.
Майкл Гомер

Я разместил снимок. @ MichaelHomer
Исчисление

Спасибо! Я поэкспериментировал с форматированием, чтобы оно совпадало - это все еще не совсем правильно, но это то, что говорит картинка.
Майкл Гомер

1
Отсутствует ; endif?
Данблэк

Не работает Он содержит синтаксические ошибки: без пробела [и без fiв конце. Кроме того, %является особенным в crontabs.
Кусалананда

Ответы:


17

абстрактный

Правильный код должен быть:

#!/bin/sh
[ "$#" -eq 0 ] && echo "Usage: $0 command [args]" && exit 1
[ "$(date -d tomorrow +'%d')" = 01 ] || exit 0
exec "$@"

Вызовите этот скрипт, end_of_month.shи вызов в cron просто:

00 12 28-31 * * /path/to/script/end_of_month.sh command

Это запустит сценарий end_of_month(который внутренне проверит, что день является последним днем ​​месяца) только в дни 28, 29, 30 и 31. Нет необходимости проверять конец месяца в любой другой день.

Старый пост.

Это цитата из книги Ричарда Блюма «Командная строка Linux и библейские сценарии», Кристина Бреснахан, стр. 442, третье издание, John Wiley & Sons © 2015.

Да, это то, что говорится, но это неправильно / неполно:

  • Отсутствует закрытие fi.
  • Необходимо пространство между [и следующим `.
  • Он настоятельно рекомендуется использовать $ (...) вместо `…`.
  • Важно использовать кавычки вокруг расширений, таких как"$(…)"
  • Есть дополнительный ;послеthen

Откуда мне знать? (ну, по опыту ☺) но вы можете попробовать Shellcheck . Вставьте код из книги (после звездочек), и он покажет вам ошибки, перечисленные выше, плюс «недостающий шебанг». Сценарий без каких-либо ошибок в Shellcheck это:

#!/bin/sh if [ "$(date +%d -d tomorrow)" = 01 ] ; then script.sh; fi

Этот сайт работает, потому что написано "код оболочки". Это синтаксис, который работает во многих оболочках.

Некоторые проблемы, которые не затрагивает shellcheck:

  • Предполагается, что команда date является версией даты GNU. Тот, у которого есть -dопция, которая принимает tomorrowв качестве значения (busybox имеет опцию -d, но не понимает завтра, а BSD имеет -dопцию, но не связана с «отображением» времени).

  • Лучше установить формат после всех опций date -d tomorrow +'%d'.

  • Время запуска cron всегда указывается по местному времени, что может означать, что одно задание запускается на 1 час раньше, чем точный счетчик дней, если установлено летнее время (DST) или не установлено.

То, что мы сделали, - это скрипт оболочки, который можно вызвать с помощью cron. Мы можем дополнительно изменить скрипт, чтобы он принимал аргументы программы или команды для выполнения, например так (наконец, правильный код):

#!/bin/sh
[ "$#" -eq 0 ] && echo "Usage: $0 command [args]" && exit 1
[ "$(date -d tomorrow +'%d')" = 01 ] || exit 0
exec "$@"

Вызовите этот скрипт, end_of_month.shи вызов в cron просто:

00 12 28-31 * * /path/to/script/end_of_month.sh command

Это запустит сценарий end_of_month(который внутренне проверит, что день является последним днем ​​месяца) только в дни 28, 29, 30 и 31. Нет необходимости проверять конец месяца в любой другой день.

Убедитесь, что указан правильный путь. PATH внутри cron не будет (маловероятно) таким же, как пользовательский PATH.

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

Это также позволит избежать дополнительной проблемы, которую cron генерирует с полной командной строкой:

  • Cron разделяет командную строку на любую, %даже если она заключена в кавычки или 'или "(только здесь \работает). Это распространенный способ провалов заданий cron.

Вы можете проверить, end_of_month.shправильно ли работает скрипт на какой-либо дате (не дожидаясь конца месяца, чтобы обнаружить, что он не работает), протестировав его с помощью faketime:

$ faketime 2018/10/31 ./end_of_month echo "Command will be executed...."
Command will be executed....

ast-open date(или dateвстроенная в ksh93, если ksh93 была построена как часть ast-open) поддерживает date -d tomorrow +%sили date +%s tomorrow).
Стефан Шазелас

2
Опыт доказал (много раз), что гораздо лучше протестировать скрипт, правильно работающий с поддельным временем, чем ждать до конца месяца, чтобы обнаружить, что запланированное задание не сработало. Например, обнаружение, что это не * * * * * echo "$(date -u +'date %c')" >>~/testfileсработает, потому что забыли процитировать \%(что становится трудно отлаживать, если возможна только одна попытка в месяц). @ Кусалананда
Исаак

8

Предполагая, что синтаксические ошибки исправлены, и команда немного переформулирована, чтобы быть менее многословной:

00 12 28-31 * * [ "$( date -d tomorrow +\%d )" != "01" ] || command1

Это выполняется date +%d -d tomorrow(при условии, что используется GNU date), чтобы получить завтрашнюю дату в виде двузначного числа. Если число не 01, то сегодня не последний день месяца. В этом случае испытания успешно , и command1это не выполняется. Работа выполняется в полдень в дни, которые могут быть последним днем ​​месяца.

Оригинальная команда:

00 12 * * * if [`date +%d -d tomorrow` = 01 ] ; then ; command1

Это имеет несколько проблем:

  • Нет места после [.
  • А ;сразу после then.
  • %является особенным в спецификациях работы cron и должен быть экранирован как \%(см. man 5 crontab).
  • В fiконце нет финала , совпадающего с if.

2
Проблема с использованием [ ... ] && command1вместо этого if...состоит в том, что в дни, не являющиеся последним днем ​​месяца, задание cron заканчивается с ненулевым состоянием выхода, и о сбое может потребоваться сообщить. Использование [ "$(...)" != 01 ] || command1это еще один способ избежать проблемы.
Стефан Шазелас

1
Еще одна проблема с исходным кодом - ;между thenи command1.
Стефан Шазелас

@ StéphaneChazelas Еще одна причина, почему я не люблю однострочники, их трудно читать.
Кусалананда

Разница во времени между последовательными запусками команды может не быть целым числом (24 часа) дней, поскольку изменение летнего времени сместит время начала cron.
Исаак

3
@cat Неважно, как вы цитируете, "или '(кроме \), знак процента %заставит cron разбить строку на две части. Это один из обычных способов заставить cron потерпеть неудачу.
Исаак

-1

Для того, чтобы запланировать в последний день каждого месяца, вы можете попробовать: 0 0 15,L * *.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.