Cron-Ausdrucke entmystifiziert: funf Felder, Bereiche, Schritte und die Wochentag-ODER-Falle

Die funf Felder und ihre gueltigen Wertebereiche

Jeder Standard-Cron-Ausdruck besteht aus genau funf durch Leerzeichen getrennten Feldern, die von links nach rechts gelesen werden: Minute (0-59), Stunde (0-23), Tag des Monats (1-31), Monat (1-12) und Wochentag (0-7). Die Minute ist immer das am weitesten links stehende Feld - ein haufiger Fehler ist es, eine Cron-Zeile von rechts nach links zu lesen, was die Bedeutung vollstandig umkehrt. Das Wochentag-Feld akzeptiert sowohl 0 als auch 7 fur Sonntag, eine bewusste Redundanz aus der Unix-Tradition.

In Implementierungen, die den Vixie-Cron-Konventionen folgen, akzeptiert das Monatsfeld auch dreistellige englische Abkurzungen (jan bis dec) und das Wochentag-Feld akzeptiert sun bis sat. Fur die Portabilitat zwischen Umgebungen ist die Verwendung von Zahlenwerten sicherer. Quartz Scheduler (in Java-Anwendungen verwendet) fugt ein Sekunden-Feld vorne an, und einige Cloud-Scheduler wie AWS EventBridge fugen ein optionales siebtes Jahr-Feld hinzu - diese erweiterten Formate sind nicht mit dem POSIX-Standard mit funf Feldern austauschbar.

Platzhalter, Kommalisten, Bindestrich-Bereiche und Schrittweiten mit Schragstrich

Vier Sonderzeichen steuern die Ubereinstimmung eines Felds: Das Sternchen (*) stimmt mit jedem gultigen Wert des Felds uberein - * * * * * wird jede Minute ausgefuhrt. Das Komma (,) erstellt eine Liste diskreter Werte: 1,3,5 im Monatsfeld bedeutet Januar, Marz und Mai. Der Bindestrich (-) definiert einen inklusiven Bereich: 9-17 im Stundenfeld stimmt mit jeder Stunde von 9 bis 17 einschliesslich uberein - nutzlich fur "nur Geschaftszeiten"-Zeitplane. Diese drei Zeichen konnen kombiniert werden: 1-5,0 im Wochentag-Feld stimmt mit Montag bis Freitag plus Sonntag uberein.

Der Schragstrich (/) fuhrt einen Schrittweite-Wert ein. Nach einem Sternchen verwendet, erzeugt */15 im Minutenfeld die Menge {0, 15, 30, 45} - jedes Vielfache von 15 ab dem Feldminimum. Nach einem Bereich verwendet, erzeugt 0-30/5 die Menge {0, 5, 10, 15, 20, 25, 30}. Der Schritt beginnt immer an der linken Grenze des Bereichs (oder bei 0 fur *), niemals bei einem zufalligen Versatz. Deshalb wird */5 im Minutenfeld bei 0, 5, 10 ... ausgefuhrt, unabhangig davon, wann der Cron-Daemon gestartet wurde.

Die ODER-Falle bei Tag-des-Monats und Wochentag

Das uberraschendste Verhalten von Cron tritt auf, wenn beide Felder - Tag des Monats und Wochentag - auf Werte gesetzt sind, die kein Platzhalter sind. Intuitiv konnte man denken, 0 2 15 * 5 bedeute "um 2:00 Uhr morgens nur an Freitagen, die auf den 15. des Monats fallen" - eine UND-Bedingung. In der Praxis behandeln die meisten Cron-Implementierungen, einschliesslich Vixie Cron und dcron, dies als ODER-Bedingung: Der Auftrag wird am 15. jedes Monats um 2:00 Uhr ausgefuhrt und auch jeden Freitag um 2:00 Uhr, unabhangig vom Datum. Die Handbuchseite cron(5) dokumentiert dies explizit: "Wenn sowohl DOM als auch DOW angegeben sind, wird der Befehl ausgefuhrt, wenn eines der Felder mit der aktuellen Zeit ubereinstimmt."

Dieses Verhalten uberrascht Entwickler, die versuchen, zusammengesetzte Datumsbedingungen auszudrucken. Wenn Sie wirklich "den dritten Freitag jedes Monats" benotigen, kann Cron dies nicht direkt ausdrucken. Die idiomatische Losung besteht darin, fur jeden Freitag zu planen (0 2 * * 5) und am Anfang des Befehls einen Shell-Test hinzuzufugen: [ $(date +\%d) -ge 15 ] && [ $(date +\%d) -le 21 ] && /pfad/zum/skript. Alternativ kann ein hoherwertiger Scheduler verwendet werden, der Kalender-Wiederholungsregeln (RFC 5545 RRULE) nativ unterstutzt, wie z.B. die OnCalendar=-Direktiven von systemd-Timern.

Vordefinierte Kurzformen: @reboot, @hourly, @daily, @weekly, @monthly

Vixie Cron fuhrte eine Reihe benannter Kurzformen ein, die die Funf-Felder-Syntax fur haufige Zeitplane ersetzen. @reboot fuhrt den Auftrag einmalig unmittelbar nach dem Start des Cron-Daemons aus - nutzlich fur leichtgewichtige Startaufgaben, obwohl fur Produktionsdienste ein geeignetes Init-System (systemd-Unit usw.) vorzuziehen ist. @hourly entspricht 0 * * * *. @daily (Alias @midnight) entspricht 0 0 * * *; @weekly entspricht 0 0 * * 0 (Sonntag Mitternacht); @monthly entspricht 0 0 1 * * (Mitternacht am ersten des Monats); und @yearly (Alias @annually) entspricht 0 0 1 1 * (Mitternacht am 1. Januar).

Diese Kurzformen verbessern die Lesbarkeit von crontab-Dateien, sind aber davon abhangig, dass der Cron-Daemon die @-Syntax erkennt - POSIX definiert sie nicht, daher sind sie in einigen minimalen oder eingebetteten Cron-Implementierungen nicht verfugbar. Beim Schreiben von Cron-Ausdrucken fur Cloud-Scheduler (AWS EventBridge, Google Cloud Scheduler, GitHub Actions) sollte die Anbieterdokumentation konsultiert werden, da die meisten Cloud-Plattformen die @-Kurzformen nicht implementieren und das explizite Funf-Felder-Format erfordern.

Schrittweiten und die Falle "nicht alle N Einheiten ab jetzt"

Ein haufiges Missverstandnis uber die Schrittweiten-Syntax ist, */N als "alle N Einheiten ab Erstellung dieses Auftrags" zu behandeln. Das ist nicht der Fall. Die Schrittweite ist ein Modulo-Filter auf absolute Zeitwerte. */5 im Minutenfeld ist eine Kurzform fur die Menge {x : x mod 5 = 0, 0 <= x <= 59} = {0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55} - zwolf feste Uhrzeitpositionen pro Stunde. Der Cron-Daemon vergleicht diese Positionen mit der aktuellen Uhrminute; er hat kein Gedachtnis daran, wann Ihr Auftrag registriert wurde.

Dies erzeugt ein subtiles Problem mit 0 */6 * * *. Dieser Ausdruck wird zu den Stunden 0, 6, 12 und 18 ausgefuhrt - Mitternacht, 6 Uhr, Mittag und 18 Uhr - genau viermal pro Tag. Wenn Sie diesen Auftrag um 15 Uhr hinzufugen und "die erste Ausfuhrung sechs Stunden spater um 21 Uhr" erwarten, werden Sie uberrascht sein: Die nachste Ausfuhrung ist um 18 Uhr (Stunde 18). Wenn Sie eine Ausfuhrung um 21 Uhr benotigen, verwenden Sie die explizite Liste 0 9,15,21,3 * * * oder andern Sie die Startstunde mit 0 3/6 * * * - beachten Sie jedoch, dass die Notation START/SCHRITT eine Vixie-Cron-Erweiterung ist; fur maximale Portabilitat sollten Sie immer die explizite kommagetrennte Liste verwenden.

Zeitzonen: der unsichtbare Fehler in Cron-Zeitplanen

Traditionelle Cron-Daemons fuhren alle Aufgaben in der Systemzeitzone der Maschine aus. Auf einem Server, der auf UTC konfiguriert ist - was der Standard fur die meisten Docker-Container-Basis-Images und viele Cloud-VMs ist - bedeutet 0 8 * * 1-5 nicht 8 Uhr in Ihrer Ortszeit; es bedeutet 8 Uhr UTC. Wenn Ihr Team in UTC+9 (Japanische Standardzeit) ist, wird dieser Auftrag um 17 Uhr Ortszeit ausgefuhrt. AWS CloudWatch Events (jetzt EventBridge) Cron-Ausdrucke sind als nur UTC dokumentiert. on: schedule: von GitHub Actions ist ebenfalls UTC.

Die Sommerzeit (DST) fuhrt zu zwei weiteren Fehlermodellen. Wenn die Uhren vorgestellt werden (z.B. von 01:59 auf 03:00), wird ein fur die ubersprungene Stunde geplanter Cron-Auftrag an diesem Tag einfach nicht ausgefuhrt. Wenn die Uhren zuruckgestellt werden (von 02:59 auf 02:00), erscheint dieselbe Minute zweimal; ob der Auftrag ein- oder zweimal ausgefuhrt wird, hangt von der Daemon-Implementierung ab. Die sicherste Produktionsstrategie ist es, Cron-Server dauerhaft in UTC zu betreiben und die Ortszeit in der Anwendungsschicht zu konvertieren, oder einen Scheduler zu verwenden, der einen expliziten TimeZone-Parameter akzeptiert, wie z.B. systemd-Timer mit TimeZone=, Kubernetes CronJob mit .spec.timeZone (in Kubernetes 1.25 hinzugefugt) oder AWS EventBridge Scheduler mit IANA-Zeitzonennamen.