Wie kann ich eine Stunde später jeden Tag einen Cronjob starten?

16

Ich muss jeden Tag einen Cronjob beginnen, aber eine Stunde später jeden Tag. Bis auf einen Tag im Jahr funktioniert das, was ich bisher habe, größtenteils:

0 0 * * * sleep $((3600 * (10#$(date +\%j) \% 24))) && /usr/local/bin/myprog

Wenn der Tag des Jahres 365 ist, beginnt der Job um 5:00 Uhr, aber der nächste Tag (ohne Schaltjahr) hat einen Tag des Jahres als 1, sodass der Job um 1:00 Uhr beginnt. Wie kann ich diesen Eckfall loswerden?

Birke
quelle
1
Gibt es einen Grund, es nicht alle 25 Stunden zu starten?
HalosGhost
7
Und wie genau würdest du das tun? * / 25 in der Stundenposition wird es nicht lösen.
Birke
@HalosGhost Danke für deinen Vorschlag! Ich habe eine einfache Implementierung basierend auf at geschrieben.
Giulio Muscarello

Antworten:

23

Meine bevorzugte Lösung wäre, den Job stündlich zu starten, aber das Skript selbst zu überprüfen, ob es Zeit für die Ausführung ist oder nicht, und zu beenden, ohne 24 von 25 Schritten.

crontab:

0 * * * *    /usr/local/bin/myprog

oben auf myprog:

[ 0 -eq $(( $(date +%s) / 3600 % 25 )) ] || exit 0

Wenn Sie keine Änderungen am Skript selbst vornehmen möchten, können Sie auch den Check "time to run" in den crontab-Eintrag einfügen, der jedoch zu einer langen, unansehnlichen Zeile führt:

0 * * * *    [ 0 -eq $(( $(date +\%s) / 3600 \% 25 )) ] && /usr/local/bin/myprog
Celada
quelle
1
'%' s müssen mit einem Backslash in der Crontab entkommen.
Birke
@ Birch Ich wusste das nie! Ich glaube, ich habe noch nie versucht, einen Prozentwert in eine Crontab aufzunehmen. Danke für die Korrektur, ich habe die Antwort bearbeitet.
Celada
1
Das sieht für mich gut aus. Ich habe keine Ahnung, was das OP in Bezug auf die Sommerzeit tun möchte (Frühling voraus, Herbst zurück), aber das OP sollte entsprechende Anpassungen vornehmen.
Emory
Ich würde mir ein wenig Sorgen machen, ob ich in der Division rund werde. Wenn die datevom Kernel zurückgegebene Zeit aus irgendeinem Grund 1msvor der erwarteten Ausführungszeit des Skripts liegt, führt die Überprüfung zu einem falschen Ergebnis.
Kasperd
1
@kasperd Ich weiß nicht, ich nehme an, Sie haben Recht, wenn verschiedene CPUs unterschiedliche Zeiten melden. Was das angeht ntpd, ist es schwierig, die Uhr nur zu drehen, nicht zu springen, um diese Art von Problem zu vermeiden, aber Sie haben Recht, sie (oder ntpdate) kann manchmal die Zeit zurückspringen. Was die cronfalsche Berechnung der Schlafverzögerung angeht, bin ich mir ziemlich sicher, dass dies als Fehler angesehen werden würde! Dennoch Punkt genommen, und eine Behelfslösung des Job 30 Minuten nach der vollen Stunde einzuplanen wäre , die weniger wahrscheinlich , das Problem zu verursachen ... oder ± 1800 in dem arithmetischen Ausdruck hinzufügen , bevor die reamainder mod 3600. nehmen
Celada
7

Wenn Ihr System über systemd verfügt, können Sie hierfür Timer-Ereignisse verwenden. Definieren Sie einfach einen neuen Dienst , der den Befehl / die Aufgabe enthalten soll, den / die Sie ausführen möchten, und erstellen Sie dann ein Timer-Ereignis mit der OnUnitActiveSecOption:

[Unit]
Description=daily + 1 hour task

[Timer]
OnUnitActiveSec=25h # run 25 hours after service was last started
AccuracySec=10min

[Install]
WantedBy=timers.target

Verwenden Sie den gleichen Namen für die Dateien, außer dass .serviceSie ihn verwenden .timer.

Synthese:

  1. Erstellen Sie eine Datei mit dem Namen job.serviceim /etc/systemd/system/Verzeichnis.
  2. Füllen Sie es mit den notwendigen Informationen. Sie können die Konfiguration mit überprüfen systemctl status job.service.
  3. Erstellen Sie eine Datei mit dem Namen job.timerin /etc/systemd/system/.
  4. Füllen Sie es mit den erforderlichen Informationen:

    [Unit]
    Description=daily + 1 hour task
    
    [Timer]
    OnUnitActiveSec=25h # run 25 hours after service was last started
    AccuracySec=10min
    
    [Install]
    WantedBy=timers.target
    
  5. Überprüfen Sie den Timer mit systemctl list-timers
  6. Erledigt.
Braiam
quelle
Wenn ich von der Einfachheit von cron abweichen würde, würde ich launchd verwenden, was weniger Arbeit erfordern würde als Ihr Beispiel für systemd.
Birke
6

Wenn es Ihnen nichts ausmacht, etwas anderes als Cronjobs zu verwenden, würde ich das weniger bekannte Dienstprogramm vorschlagen at. Schreiben Sie einfach ein Wrapper-Skript, das sich beide selbst für die Ausführung in 25 Stunden einplant, und rufen Sie dann Ihr Programm auf. Dies scheint die sauberste Lösung zu sein.
Zum Beispiel könnten Sie dies in ~ / script.sh schreiben:

echo "bash ~/script.sh" | at now + 25 hours
/usr/bin/yourprogram

Und dann einfach bash ~/script.shmal laufen .

Vielen Dank an @HalosGhost für die Idee, den Job einmal in 25 Stunden zu planen.

Giulio Muscarello
quelle
2
Es gibt zwei Probleme bei der Verwendung atfür diesen Zweck: (1) Wenn der Job auch nur einmal nicht korrekt ausgeführt wird, kann er sich wahrscheinlich auch nicht neu terminieren, und dann werden nicht nur die nächste Ausführung, sondern alle zukünftigen Ausführungen effektiv abgebrochen, bis eine Person dies bemerkt (2) Da die Ausführung des Jobs einige now + 25 hoursZeit in Anspruch nimmt, die nicht Null ist, wird sie bei Verwendung der naiven Methode jedes Mal einige Sekunden (oder länger) später ausgeführt, und diese Verzögerung nimmt mit der Zeit zu, so dass sie schließlich völlig falsch ausgeführt wird Zeit.
Celada
2
Sie haben Recht mit # 1; Bei Nummer 2 bin ich mir nicht so sicher. Obwohl ich keine Daten habe, denke ich nicht, dass die Verzögerung zwischen dem Umstellen der Uhr und dem Auslösen und anschließenden Neuplanen des Auftrags groß genug ist, um erkennbar zu sein - insbesondere angesichts der Tatsache, dass die Auflösung atauf Minuten begrenzt ist und alle Aufträge startet um [Zeit]: 00.
Giulio Muscarello
1
@ Celada: Die Planung des nächsten atJobs als erstes im at-Skript vermeidet diese Probleme. Ungeachtet dessen, wenn ein Fehler auftritt, ist die gesamte Kette unterbrochen, was möglicherweise nicht erwünscht ist: Wenn der Anwendungsfall "nur ausführen, wenn es funktioniert" lautet, ist ein Nicht-Neustart eine großartige Funktion. Wenn der Anwendungsfall jedoch "immer ausgeführt wird, auch wenn der letzte fehlgeschlagen ist", atist dies nicht das richtige Werkzeug.
Bischof