it-swarm.com.ru

Как поручить cron выполнять работу каждую вторую неделю?

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

0 6 * * Tue

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

51
tkokoszka

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

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

Ответ

Измените свою логику 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) ,.

41
pilcrow

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

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

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

7
notorious.dds

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

Первый cronjob:

0 0 8 ? 1/1 TUE#1 *

Второй cronjob:

0 0 8 ? 1/1 TUE#3 *

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

2
Hans

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

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

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

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

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

  • 1:00 по местному времени = 11:00 UTC вчера
  • 10 вечера по местному времени = 8 вечера по UTC сегодня

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

Чтобы обойти это, нам нужно рассчитать смещение от UTC на основе нашего local часового пояса, а не 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
1
dxdc

Как насчет каждой третьей недели?

Вот мое предложение:

0 6 * * Tue expr `date +\%W` \% 3 == 0 > /dev/null || /scripts/fortnightly.sh

... или же ...

0 6 * * Tue expr `date +\%W` \% 3 == 1 > /dev/null || /scripts/fortnightly.sh

... или конечно ...

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

... в зависимости от ротации недели.

0
macetw

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

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- Если остаток равен нулю, вы находитесь на двухнедельном цикле

0
Paul Lemmons