web-dev-qa-db-de.com

Wie kann man cron anweisen, alle zwei Wochen einen Job auszuführen?

Ich würde gerne einen Job durch cron ausführen, der jeden zweiten Dienstag zu einer bestimmten Tageszeit ausgeführt wird. Für jeden Dienstag ist es einfach:

0 6 * * Tue

Aber wie mache ich es "jeden zweiten Dienstag" (oder wenn Sie es vorziehen - jede zweite Woche)? Ich möchte keine Logik in das Skript selbst implementieren, sondern die Definition nur in cron beibehalten.

51
tkokoszka

Wie wäre es damit, es behält es in crontab, auch wenn es in den ersten fünf Feldern nicht genau definiert ist:

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

Antworten

Ändern Sie Ihre Dienstag-Cron-Logik so, dass sie jede zweite Woche seit der Epoche ausgeführt wird.

Zu wissen, dass es 604800 Sekunden in einer Woche gibt (DST-Änderungen und Schaltsekunden ignorieren, danke) und Datum GNU verwenden:

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

Beiseite

Kalender-Arithmetik ist frustrierend.

Die Antwort von @ xahtep ist großartig, aber wie @Doppelganger in den Kommentaren , wird sie an bestimmten Jahresgrenzen scheitern. Keiner der "week of year" -Kennzeichner von date kann hier helfen. An einem Dienstag Anfang Januar wird sich die Wochenparität des letzten Dienstag des Vorjahres zwangsläufig wiederholen: 2016-01-05 (% V), 2018-01-02 (% U) und 2019-01-01 (% W) .

41
pilcrow

Pilcrows Antwort ist großartig. Es führt jedoch dazu, dass das vierzehntägige.sh-Skript alle even - Woche (seit der Epoche) ausgeführt wird. Wenn Sie das Skript in ungeraden Wochen ausführen möchten, können Sie seine Antwort etwas anpassen:

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

Wenn Sie die 1 in eine 0 ändern, wird sie wieder in geraden Wochen verschoben.

7
notorious.dds

Vielleicht ein bisschen dumm, aber man könnte auch zwei Cronjobs erstellen, einen für jeden ersten Dienstag und einen für jeden dritten.

Erster Cronjob:

0 0 8 ? 1/1 TUE#1 *

Zweiter Cronjob:

0 0 8 ? 1/1 TUE#3 *

Ich bin nicht sicher über die Syntax hier, ich habe http://www.cronmaker.com/ verwendet, um diese zu erstellen.

2
Hans

Ich habe einige zusätzliche Einschränkungen der obigen Ansätze entdeckt, die in einigen Edge-Fällen fehlschlagen können. Betrachten Sie zum Beispiel:

@xahtep und @Doppelganger erörterten Probleme mit %W an bestimmten Jahresgrenzen.

@pilcrows Antwort umgeht dies bis zu einem gewissen Grad, wird jedoch auch an bestimmten Grenzen versagen. Antworten in diesem oder in anderen verwandten Themen verwenden die Anzahl von Sekunden pro Tag (im Vergleich zur Woche), die aus denselben Gründen auch an bestimmten Grenzen versagen.

Dies liegt daran, dass diese Ansätze von der UTC-Zeit abhängen (Datum +% s). Stellen Sie sich einen Fall vor, bei dem wir jeden 2. Dienstag um 1 und 22 Uhr einen Job ausführen.

Angenommen, GMT-2:

  • 1 Uhr Ortszeit = 23 Uhr UTC gestern
  • 22 Uhr Ortszeit = 20 Uhr UTC heute

Wenn wir jeden Tag nur einmal überprüfen, ist dies kein Problem, aber wenn wir mehrmals prüfen - oder wenn wir uns kurz vor UTC befinden, werden Zeit und Sommerzeit herbeigeführt gleicher Tag.

Um dies zu umgehen, müssen wir einen Offset von UTC basierend auf unserer local - Zeitzone und nicht mit UTC berechnen. Ich habe in BASH keine einfache Methode dafür gefunden. Daher habe ich eine Lösung entwickelt, die einen schnellen Einzeiler in Perl verwendet, um den Offset von UTC in Sekunden zu berechnen.

Dieses Skript nutzt date +% z, um die lokale Zeitzone auszugeben.

Bash-Skript:

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 ))

dann, um festzustellen, ob der Tag gerade oder ungerade ist:

if [ ${DAY_PARITY} -eq 1 ]; then
...
else
...
fi
1
dxdc

Wie sieht es mit jeder 3. Woche aus?

Hier mein Vorschlag:

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

... oder ...

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

... oder natürlich ...

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

... abhängig von der Wochenrotation.

0
macetw

Wenn Sie dies basierend auf einem bestimmten Startdatum tun möchten:

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

Sollte jeden zweiten Montag ab 2018-03-19 feuern

Ausdruck liest: Montags um 6 Uhr morgens, wenn ...

1 - Holen Sie sich das heutige Datum in Sekunden, geteilt durch die Anzahl der Sekunden in einem Tag, um es in Tage als Epoche umzuwandeln

2 - Machen Sie dasselbe für das Startdatum und konvertieren Sie es in die Anzahl der Tage seit der Epoche

3 - Holen Sie sich den Unterschied zwischen den beiden

4 - durch 14 dividieren und den Rest überprüfen

5- Wenn der Rest gleich Null ist, befinden Sie sich im zweiwöchigen Zyklus

0
Paul Lemmons