Как поручить cron выполнять работу каждую вторую неделю? - PullRequest
53 голосов
/ 08 декабря 2008

Я бы хотел запустить задание через cron, которое будет выполняться каждый второй вторник в указанное время дня. Для каждого вторника это просто:

0 6 * * Tue

Но как сделать это "каждый второй вторник" (или, если хотите, каждую вторую неделю)? Я не хотел бы реализовывать какую-либо логику в самом скрипте, но оставляю определение только в cron.

Ответы [ 11 ]

41 голосов
/ 09 октября 2013

Ответ

Измените свою логику cron вторника, чтобы она выполнялась каждую вторую неделю с эпохи .

Зная, что в неделе 604800 секунд (игнорируя изменения летнего времени и дополнительные секунды, спасибо) и используя дату GNU:

0 6 * * Tue expr `date +\%s` / 604800 \% 2 >/dev/null || /scripts/fortnightly.sh

Помимо

Календарная арифметика расстраивает.

@ Ответ xahtep потрясающий, но, как отметил @ Doppelganger в комментариях , он не получится на определенных границах года. Здесь не поможет ни один из спецификаторов недели недели утилиты date. В какой-то вторник в начале января неизбежно будет повторяться недельное паритетное значение последнего вторника предыдущего года: 2016-01-05 (% V), 2018-01-02 (% U) и 2019-01-01 (% W) .

40 голосов
/ 08 декабря 2008

Как насчет этого, он сохраняет его в crontab, даже если он не определен точно в первых пяти полях:

0 6 * * Tue expr `date +\%W` \% 2 > /dev/null || /scripts/fortnightly.sh
7 голосов
/ 20 октября 2015

ответ Пилкроу отлично. Однако это приводит к тому, что скрипт fortnightly.sh запускается каждую даже неделю (начиная с эпохи). Если вам нужен скрипт для запуска в нечетные недели, вы можете немного подправить его ответ:

0 6 * * Tue expr \( `date +\%s` / 604800 + 1 \) \% 2 > /dev/null || /scripts/fortnightly.sh

Изменение 1 на 0 вернет его к четным неделям.

2 голосов
/ 09 октября 2013

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

Первый cronjob:

0 0 8 ? 1/1 TUE#1 *

Второй cronjob:

0 0 8 ? 1/1 TUE#3 *

Не уверен насчет синтаксиса здесь, я использовал http://www.cronmaker.com/ для их создания.

1 голос
/ 15 ноября 2018

Если вы хотите сделать это на основе заданной даты начала:

0 6 * * 1 expr \( `date +\%s` / 86400 - `date --date='2018-03-19' +\%s` / 86400 \) \% 14 == 0 > /dev/null && /scripts/fortnightly.sh

Должен срабатывать каждый второй понедельник, начиная с 2018-03-19

Выражение гласит: бегать в 6 утра по понедельникам, если ...

1 - Получить сегодняшнюю дату в секундах, разделенную на количество секунд в дне, для преобразования в дни эпохи дней

2 - сделать то же самое для даты начала, преобразовав ее в количество дней с начала эпохи

3 - Получите разницу между двумя

4 - разделить на 14 и проверить остаток

5- Если остаток равен нулю, вы находитесь на двухнедельном цикле

1 голос
/ 21 декабря 2016

Я обнаружил некоторые дополнительные ограничения вышеуказанных подходов, которые могут не работать в некоторых крайних случаях. Например, рассмотрим:

@ xahtep и @Doppelganger обсуждали проблемы, используя %W на определенных границах года выше.

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

Это потому, что эти подходы основаны на времени UTC (дата +% s). Рассмотрим случай, когда мы выполняем работу в 1:00 и 22:00 каждый второй вторник.

Предположим, GMT-2:

  • 1:00 по местному времени = 23:00 UTC вчера
  • 22:00 по местному времени = 20:00 UTC сегодня

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

Чтобы обойти это, нам нужно рассчитать смещение от UTC на основе нашего местного часового пояса, а не UTC. Я не смог найти простой способ сделать это в BASH, поэтому я разработал решение, которое использует быструю однострочную в Perl для вычисления смещения от UTC в считанные секунды.

Этот скрипт использует дату +% ​​z, которая выводит местный часовой пояс.

Bash скрипт:

TZ_OFFSET=$( date +%z | perl -ne '$_ =~ /([+-])(\d{2})(\d{2})/; print eval($1."60**2") * ($2 + $3/60);' )
DAY_PARITY=$(( ( `date +%s` + ${TZ_OFFSET} ) / 86400 % 2 ))

затем, чтобы определить, является ли день четным или нечетным:

if [ ${DAY_PARITY} -eq 1 ]; then
...
else
...
fi
0 голосов
/ 23 ноября 2016

Если вы хотите каждый вторник только на второй неделе:

00 06 * * 2 # 2

0 голосов
/ 26 ноября 2013

Почему не что-то вроде

0 0 1-7,15-21,29-31 * 5

Это уместно?

0 голосов
/ 06 мая 2013

Синтаксис "/ 2" не совпадает с днем ​​недели. Так что мой добавленный к смарту выше - просто использовать 1,15 в поле MonthDay.

0 6 1,15 * * /scripts/fornightly.sh> / dev / null 2> & 1

0 голосов
/ 22 февраля 2013

Cron предоставляет синтаксис «каждый второй» «/ 2». Просто следуйте за соответствующим полем единицы времени с "/ 2", и он выполнит cronjob "каждый второй раз". В вашем случае ...

0 6 * * Tue/2

Вышеуказанное должно выполняться каждый вторник.

...