Как я могу сказать cron запускать команду через день (нечетное / четное)

45

При настройке cron для запуска команды через день, используя поле «День месяца», например:

1 22 */2 * * COMMAND

он запускается каждый раз, когда день месяца нечетный: 1,3,5,7,9 и так далее.

Как настроить cron для работы в дни месяца, которые даже равны 2,6,8,10 и т. Д. (Без буквального указания этого, что проблематично, поскольку в каждом месяце разное количество дней в месяце)?

Фредди
источник

Ответы:

60

Синтаксис, который вы пробовали, на самом деле неоднозначен. В зависимости от того, сколько дней в месяце, некоторые месяцы будут работать в нечетные дни, а некоторые - в четные. Это потому, что способ расчета рассчитывает общее количество возможностей и разделяет их. Вы можете переопределить это поведение strage-ish, вручную указав диапазон дней и используя нечетное или четное количество дней. Поскольку четные дневные сценарии никогда не будут выполняться в 31-й день более длинных месяцев, вы ничего не потеряете, используя 30 дней в качестве основы для четных дней, и, указав специально для деления, как если бы было 31 день, вы можете вызвать нечетные исполнение.

Синтаксис будет выглядеть так:

# Will only run on odd days:
0 0 1-31/2 * * command

# Will only run on even days:
0 0 2-30/2 * * command

Ваше беспокойство по поводу того, что месяцы не имеют одинакового количества дней, здесь не имеет значения, потому что ни в одном месяце не будет БОЛЬШЕ дней, чем этот, а для плохого февраля диапазон дат просто не будет совпадать с последним днем ​​или двумя, но это не принесет вреда, если это в списке.

Единственный «уловка» для этого подхода состоит в том, что если вы находитесь в нечетном цикле дня, в следующие месяцы с 31 днем ​​ваша команда также будет выполняться в первом месяце. Точно так же, если вы выполняете четный цикл, каждый високосный год будет вызывать один трехдневный цикл и конец февраля. Вы не можете обойти тот факт, что любой регулярный паттерн «каждый второй день» не всегда будет приходиться на четные или нечетные дни в каждом месяце, и как бы вы ни принудили это сделать, у вас либо будет дополнительный прогон, либо вы пропустите пробежку между месяцы с несоответствующим количеством дней.

Калеб
источник
1
Спасибо, но что будет в такие месяцы, как февраль, когда у вас всего 28 дней? Звезда на самом деле заботится об этом - но это действительно неоднозначно.
Фредди
3
@freddie: Смотрите мой отредактированный ответ ... но это не проблема, потому что значения за пределами диапазона будут просто игнорироваться, ничего не произойдет 30 или 31 февраля. Когда-либо. Вы можете указать вручную с помощью списка, 0,2,4...,30,32,34и это не будет иметь значения, значения вне диапазона просто никогда не будут совпадать.
Калеб
1
Спасибо! Я понимаю, спасибо за информативный ответ!
Фредди
3
На сервере Ubuntu 8.04 кажется, что синтаксис, использующий ноль дня месяца, недопустим (плохой день месяца). Однако принят следующий синтаксис:0 0 2-30/2 * * command
1
Fedora и RHEL 5,6,7 также не любят 0 как день месяца. Как указал user31053: 0 0 2-30/2 * * commandработает как положено.
NoelProf
2

Я думаю, что есть возможность использовать день года, например, так:

# for odd days
test $(((`date +%j` % 2))) != 0 && command

# for even days
test $(((`date +%j` % 2))) == 0 && command

Он протестирован для систем Unix и Linux.

Jordi
источник
Я предпочитаю этот ответ, потому что он пропускает проблему с нечетным числом дней с некоторыми месяцами. Во всяком случае, я предлагаю использовать обозначение доллара вместо обратных галочек:test $(($(date +%j) % 2)) == 0 && command
caligari
Я рассмотрел, что% j не является юлианской датой, поэтому лучший код, избегающий новогоднего перехода, должен быть рассчитан с секундами:test $(($(date +%s) / 86400 % 2)) == 0 && command
caligari
Спасибо за комментарии! Наше предложение сработало для нас как шарм. Мы использовали эту схему для запуска ежедневных крон на разных узлах сервера, все они совместно используют один и тот же кронтаб, но скрипт позволяет запускать скрипт только на одном из них. Однако, если нам нужно сделать это более конкретным, мы рассмотрим ваше предложение. Спасибо!!!
Джорди
1

Давайте каждый день проверять, является ли это «другим» :-) ( bcтребуется программа)

0 0 * * * test $(echo `date +%s` / 86400 % 2 == 0 |bc) -eq 0 && command

(Я не уверен, что код отображается правильно. date +%sЧасть находится между обратными апострофами.)

user37264
источник
Это будет работать через день, но не отвечает на вопрос! Он все еще будет работать иногда в нечетные дни, иногда в четные дни в зависимости от месяца. Это имеет тот же результат, что и код в вопросе, вы просто получаете результат, выполняя свою собственную математику за секунды с начала эпохи. Это выполняется в четные дни с эпохи, но не в четные дни нашего календаря.
Калеб
1

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

Создание простого файла состояния, сообщающего о последнем запуске и последующем сравнении, будет работать очень легко.

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

Клас Маттссон
источник