Cron: Letzter Freitag im Monat

16. Dezember 2012 at 10:00

Viele regelmäßig Aufgaben erledigen Admins mit Script, die per Cron gestartet werden. Solange sich das Zeitmuster im Format von Cron eingeben läßt, ist das alles kein Problem.

  • 0 9 * * * = Jeden Tag um 9 Uhr
  • 30 14 * * 1-5 = Wochentags um 14:30 Uhr

Aber was, wenn ein Script z.B. am letzten Freitag im Monat gestartet werden soll? Das läßt sich in diesem Muster nicht darstellen. Es gibt aber ein paar einfache Tricks, die in Kombination zum Ziel dieser Aufgabe führen.

Trick 1: Kommandos mit && Verknüpfen

Mehrere Kommandos lassen sich mit && verknüpfen. Die Shell führt dabei das zweite Kommando nur aus, wenn das erste Kommando erfolgreich war (Exitcode 0):

/usr/local/daten_einsammeln.sh && /usr/local/daten_verarbeiten.sh

Somit kann man die Ausführung des zweiten Scripts (Verarbeiten der Daten) vom Erfolg des ersten Scripts (Sammeln der Daten) abhängig machen.

Trick 2: Abfragen ohne if mit [[ und ]]

Man kann mit [[ und ]] Bedingungen erstellen, ohne ‚if‘ einzusetzen. Ist die Bedingung erfüllt, wird Exitcode 0 zurückgemeldet. In Kombination mit Trick 1 läßt sich so eine bedingte Ausführung realisieren, ohne mit if-then-fi arbeiten zu müssen:

[[ bedingung ]] && kommando

Trick 3: Letzten Tag finden

Sucht man den letzten Freitag im Monat, so kann man das ganz einfach in eine Bedingung packen:

  • date -d ‚1 week‘ ‚+%m‘ ergibt den Monat des heutigen Datums.
  • date -d ‚1 week‘ ‚+%m‘ ergibt den Monat des Datums in genau einer Woche.

Jetzt einfache mathematische Logik: Wenn der Monat in genau einer Woche anders ist als heute, dann ist der heutige Wochentag der letzte in diesem Monat vorkommende.

Anders gesagt: Wenn wir in 7 Tagen in einem anderen Monat sind UND heute Freitag ist, dann ist heute der letzte Freitag im Monat.

Kombination der Tricks

Wir starten einen Cronjob jeden Freitag und nutzen den Trick 2, um das Kommando nur auszuführen, wenn eine Bedingung erfüllt ist. Die Bedingung ist aus Trick 3, wir prüfen nämlich, ob nächsten Freitag schon ein neuer Monat ist. Das sieht so aus:

[[ $(/bin/date -d '1 week' '+%m') -ne $(/bin/date '+%m') ]] && kommando

Somit haben wir einen Cronjob, der jeden Freitag gestartet wird, aber das gewünschte Kommando jeweils nur am letzten Freitag des Monats ausführt.

Hier eine Beispieldatei (/etc/cron.d/abrechnung), die Cron so akzeptieren wird:

MAILTO="cronjobs@butschek.de"
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

#  +--------------------------------------------- Minute      (0-59)
#  |      +-------------------------------------- Stunde      (0-24)
#  |      |      +------------------------------- Kalendertag (1-31)
#  |      |      |      +------------------------ Monat       (1-12)
#  |      |      |      |      +----------------- Wochentag   (1-7 = Mo-So)
#  |      |      |      |      |      +---------- Benutzer    (z.B. root)
#  |      |      |      |      |      |      +--- Kommando    (Pfad + Filename)
#  |      |      |      |      |      |      |

# Abrechnung am Letzten Freitag im Monat starten
  30     10      *      *      5      root   [[ $(/bin/date -d '1 week' '+%m') -ne $(/bin/date '+%m') ]] && /usr/local/abrechnung

#  |      |      |      |      |      |      |
#  |      |      |      |      |      |      +--- Kommando    (Pfad + Filename)
#  |      |      |      |      |      +---------- Benutzer    (z.B. root)
#  |      |      |      |      +----------------- Wochentag   (1-7 = Mo-So
#  |      |      |      +------------------------ Monat       (1-12)
#  |      |      +------------------------------- Kalendertag (1-31)
#  |      +-------------------------------------- Stunde      (0-24)
#  +--------------------------------------------- Minute      (0-59)