Lösung 1:
So beheben Sie alle Probleme/Probleme im Zusammenhang mit Crontab (Linux)
Dies ist ein Community-Wiki. Wenn Sie an dieser Antwort etwas Falsches bemerken oder zusätzliche Informationen haben, bearbeiten Sie sie bitte.
Zuerst grundlegende Terminologie:
- cron(8) ist der Daemon, der geplante Befehle ausführt.
- crontab(1) ist das Programm, das verwendet wird, um Benutzer-crontab(5)-Dateien zu ändern.
- crontab(5) ist eine benutzerspezifische Datei, die Anweisungen für cron(8) enthält.
Als nächstes Informationen zu Cron:
Jeder Benutzer auf einem System kann seine eigene crontab-Datei haben. Der Speicherort der Root- und Benutzer-crontab-Dateien ist systemabhängig, liegt aber im Allgemeinen unter /var/spool/cron
.
Es gibt einen systemweiten /etc/crontab
Datei, die /etc/cron.d
Das Verzeichnis kann crontab-Fragmente enthalten, die auch von cron gelesen und verarbeitet werden. Einige Linux-Distributionen (z. B. Red Hat) haben auch /etc/cron.{hourly,daily,weekly,monthly}
das sind Verzeichnisse, darin enthaltene Skripte, die jede Stunde/Tag/Woche/Monat mit Root-Rechten ausgeführt werden.
root kann immer den crontab-Befehl verwenden; normalen Benutzern kann Zugriff gewährt werden oder nicht. Wenn Sie die Crontab-Datei mit dem Befehl crontab -e
bearbeiten und speichern, crond prüft es auf grundsätzliche Gültigkeit, garantiert aber nicht, dass Ihre crontab-Datei korrekt aufgebaut ist. Es gibt eine Datei namens cron.deny
wodurch angegeben wird, welche Benutzer cron nicht verwenden können. Die cron.deny
Der Speicherort der Datei ist systemabhängig und kann gelöscht werden, sodass alle Benutzer cron verwenden können.
Wenn der Computer nicht eingeschaltet ist oder der crond-Daemon nicht läuft und das Datum/die Uhrzeit für die Ausführung eines Befehls abgelaufen ist, wird crond nicht aufholen und frühere Abfragen ausführen.
Crontab-Besonderheiten, wie man einen Befehl formuliert:
Ein crontab-Befehl wird durch eine einzelne Zeile dargestellt. Sie können \
nicht verwenden um einen Befehl über mehrere Zeilen zu erweitern. Der Hash (#
) stellt einen Kommentar dar, was bedeutet, dass alles in dieser Zeile von cron ignoriert wird. Führende Leerzeichen und Leerzeilen werden ignoriert.
Seien Sie SEHR vorsichtig, wenn Sie den Prozentsatz verwenden (%
) Melden Sie sich in Ihrem Befehl an. Es sei denn, sie sind \%
maskiert sie werden in Zeilenumbrüche umgewandelt und alles nach dem ersten nicht maskierten %
wird an Ihren Befehl auf stdin übergeben.
Es gibt zwei Formate für Crontab-Dateien:
-
Benutzer-Crontabs
# Example of job definition: # .---------------- minute (0 - 59) # | .------------- hour (0 - 23) # | | .---------- day of month (1 - 31) # | | | .------- month (1 - 12) OR jan,feb,mar,apr ... # | | | | .---- day of week (0 - 6) (Sunday=0 or 7) # | | | | | # * * * * * command to be executed
-
Systemweit
/etc/crontab
und/etc/cron.d
Fragmente# Example of job definition: # .---------------- minute (0 - 59) # | .------------- hour (0 - 23) # | | .---------- day of month (1 - 31) # | | | .------- month (1 - 12) OR jan,feb,mar,apr ... # | | | | .---- day of week (0 - 6) (Sunday=0 or 7) # | | | | | # * * * * * user-name command to be executed
Beachten Sie, dass letzteres einen Benutzernamen erfordert. Der Befehl wird als benannter Benutzer ausgeführt.
Die ersten 5 Felder der Zeile stellen die Zeit(en) dar, zu denen der Befehl ausgeführt werden soll. Sie können Zahlen oder ggf. Tages-/Monatsnamen in der Zeitangabe verwenden.
- Die Felder sind durch Leerzeichen oder Tabulatoren getrennt.
- Ein Komma (
,
) wird verwendet, um eine Liste anzugeben, z. B. 1,4,6,8, was bedeutet, dass sie bei 1,4,6,8 ausgeführt wird. - Bereiche werden mit einem Bindestrich angegeben (
-
) und kann mit Listen kombiniert werden, z. 1-3,9-12, was zwischen 1 und 3 bedeutet, dann zwischen 9 und 12. - Die
/
kann verwendet werden, um einen Schritt einzuleiten, z. 2/5, was bedeutet, beginnend bei 2 dann alle 5 (2,7,12,17,22...). Sie gehen nicht über das Ende hinaus. - Ein Sternchen (
*
) in einem Feld gibt den gesamten Bereich für dieses Feld an (z. B.0-59
für das Minutenfeld). - Bereiche und Schritte können kombiniert werden, z.B.
*/2
bedeutet beginnend mit dem Minimum für das relevante Feld dann alle 2 z.B. 0 für Minuten (0,2...58), 1 für Monate (1,3...11) usw.
Debuggen von Cron-Befehlen
Check die Post!
Standardmäßig sendet cron alle Ausgaben des Befehls an den Benutzer, unter dem er den Befehl ausführt. Wenn keine Ausgabe erfolgt, wird keine E-Mail gesendet. Wenn Sie möchten, dass Cron E-Mails an ein anderes Konto sendet, können Sie die Umgebungsvariable MAILTO in der Crontab-Datei festlegen, z. B.
[email protected]
1 2 * * * /path/to/your/command
Erfassen Sie die Ausgabe selbst
Sie können stdout und stderr in eine Datei umleiten. Die genaue Syntax zum Erfassen der Ausgabe kann je nach verwendetem Shell-Cron variieren. Hier sind zwei Beispiele, die alle Ausgaben in einer Datei unter /tmp/mycommand.log
speichern :
1 2 * * * /path/to/your/command &>/tmp/mycommand.log
1 2 * * * /path/to/your/command >/tmp/mycommand.log 2>&1
Sehen Sie sich die Protokolle an
Cron protokolliert seine Aktionen über Syslog, die (je nach Konfiguration) oft an /var/log/cron
gehen oder /var/log/syslog
.
Bei Bedarf können Sie die Cron-Anweisungen filtern, z. B. mit
grep CRON /var/log/syslog
Nachdem wir nun die Grundlagen von Cron besprochen haben, wo sich die Dateien befinden und wie man sie verwendet, wollen wir uns einige häufig auftretende Probleme ansehen.
Überprüfen Sie, ob Cron ausgeführt wird
Wenn cron nicht läuft, werden Ihre Befehle nicht geplant ...
ps -ef | grep cron | grep -v grep
sollte Ihnen so etwas bringen wie
root 1224 1 0 Nov16 ? 00:00:03 cron
oder
root 2018 1 0 Nov14 ? 00:00:06 crond
Wenn nicht, starten Sie es neu
/sbin/service cron start
oder
/sbin/service crond start
Es kann andere Methoden geben; Verwenden Sie, was Ihre Distribution bereitstellt.
cron führt Ihren Befehl in einer eingeschränkten Umgebung aus.
Welche Umgebungsvariablen verfügbar sind, ist wahrscheinlich sehr begrenzt. Normalerweise werden nur wenige Variablen definiert, z. B. $LOGNAME
, $HOME
, und $PATH
.
Besonders hervorzuheben ist der PATH
ist auf /bin:/usr/bin
beschränkt . Die überwiegende Mehrheit der "Mein Cron-Skript funktioniert nicht"-Probleme werden durch diesen restriktiven Pfad verursacht . Wenn sich Ihr Befehl an einem anderen Ort befindet, können Sie dies auf verschiedene Arten lösen:
-
Geben Sie den vollständigen Pfad zu Ihrem Befehl an.
1 2 * * * /path/to/your/command
-
Geben Sie in der crontab-Datei einen geeigneten PATH an
PATH=/bin:/usr/bin:/path/to/something/else 1 2 * * * command
Wenn Ihr Befehl andere Umgebungsvariablen erfordert, können Sie diese auch in der crontab-Datei definieren.
cron führt Ihren Befehl mit cwd ==$HOME
ausUnabhängig davon, wo sich das Programm, das Sie ausführen, im Dateisystem befindet, ist das aktuelle Arbeitsverzeichnis des Programms, wenn cron es ausführt, das Home-Verzeichnis des Benutzers . Wenn Sie in Ihrem Programm auf Dateien zugreifen, müssen Sie dies berücksichtigen, wenn Sie relative Pfade verwenden, oder (vorzugsweise) überall nur vollständig qualifizierte Pfade verwenden, und allen eine Menge Verwirrung ersparen.
Der letzte Befehl in meiner Crontab wird nicht ausgeführt
Cron erfordert im Allgemeinen, dass Befehle mit einer neuen Zeile abgeschlossen werden. Bearbeiten Sie Ihre Crontab; Gehen Sie zum Ende der Zeile, die den letzten Befehl enthält, und fügen Sie eine neue Zeile ein (drücken Sie die Eingabetaste).
Überprüfen Sie das Crontab-Format
Sie können keine Crontab im Benutzer-Crontab-Format für /etc/crontab oder die Fragmente in /etc/cron.d verwenden und umgekehrt. Eine benutzerformatierte Crontab enthält keinen Benutzernamen an der 6. Position einer Zeile, während eine systemformatierte Crontab den Benutzernamen enthält und den Befehl als dieser Benutzer ausführt.
Ich habe eine Datei in /etc/cron.{hourly,daily,weekly,monthly} abgelegt und sie läuft nicht
- Überprüfen Sie, dass der Dateiname keine Erweiterung hat, siehe Run-Parts
- Stellen Sie sicher, dass die Datei Ausführungsberechtigungen hat.
- Teilen Sie dem System mit, was es beim Ausführen Ihres Skripts verwenden soll (geben Sie z. B.
#!/bin/sh
ein oben)
Crondate-bezogene Fehler
Wenn Ihr Datum kürzlich durch ein Benutzer- oder Systemupdate, eine Zeitzone oder anderes geändert wurde, wird crontab anfangen, sich unregelmäßig zu verhalten und bizarre Fehler aufweisen, die manchmal funktionieren, manchmal nicht. Dies ist der Versuch von crontab zu versuchen, "das zu tun, was Sie wollen", wenn sich die Zeit darunter ändert. Das Feld „Minute“ wird nach Änderung der Stunde unwirksam. In diesem Szenario würden nur Sternchen akzeptiert. Starten Sie cron neu und versuchen Sie es erneut, ohne sich mit dem Internet zu verbinden (damit das Datum nicht auf einen der Zeitserver zurückgesetzt werden kann).
Wieder Prozentzeichen
Um den Ratschlag zu Prozentzeichen hervorzuheben, hier ein Beispiel dafür, was cron damit macht:
# cron entry
* * * * * cat >$HOME/cron.out%foo%bar%baz
erstellt die Datei ~/cron.out mit den 3 Zeilen
foo
bar
baz
Dies ist besonders aufdringlich, wenn der date
verwendet wird Befehl. Stellen Sie sicher, dass Sie die Prozentzeichen maskieren
* * * * * /path/to/command --day "$(date "+\%Y\%m\%d")"
Vorsicht vor Sudo
beim Ausführen als Nicht-Root-Benutzer,
crontab -e
öffnet die Crontab des Benutzers, während
sudo crontab -e
öffnet die Crontab des Root-Benutzers. Es wird nicht empfohlen, sudo-Befehle in einem Cron-Job auszuführen. Wenn Sie also versuchen, einen sudo-Befehl im Cron eines Benutzers auszuführen, versuchen Sie, diesen Befehl in den Cron von root zu verschieben, und entfernen Sie sudo aus dem Befehl.
Lösung 2:
Debian Linux und seine Derivate (Ubuntu, Mint usw.) haben einige Eigenheiten, die die Ausführung Ihrer Cron-Jobs verhindern können. insbesondere die Dateien in /etc/cron.d
, /etc/cron.{hourly,daily,weekly,monthly}
muss :
- im Besitz von root sein
- nur von root beschreibbar sein
- nicht von Gruppen oder anderen Benutzern beschreibbar sein
- haben einen Namen ohne Punkte '.' oder jedes andere Sonderzeichen außer '-' und '_' .
Letzteres verletzt regelmäßig ahnungslose Benutzer; insbesondere jedes Skript in einem dieser Ordner namens whatever.sh
, mycron.py
, testfile.pl
usw. werden nicht hingerichtet werden, jemals.
Dieser spezielle Punkt war meiner Erfahrung nach der mit Abstand häufigste Grund für einen nicht ausgeführten Cronjob auf Debian und Derivaten.
Siehe man cron
für weitere Details, falls erforderlich.
Lösung 3:
Wenn Ihre Cronjobs nicht mehr funktionieren, überprüfen Sie, ob Ihr Passwort abgelaufen ist, denn sobald es abgelaufen ist, werden alle Cronjobs angehalten.
Es wird Nachrichten in /var/log/messages
geben ähnlich der folgenden, die Probleme mit der Authentifizierung des Benutzers zeigt:
(username) FAILED to authorize user with PAM (Authentication token is no longer valid; new one required)
Lösung 4:
Ungewöhnliche und unregelmäßige Zeitpläne
Cron ist alles in allem ein sehr einfacher Scheduler und die Syntax erlaubt es einem Administrator nicht ohne weiteres, etwas ungewöhnlichere Zeitpläne zu formulieren.
Betrachten Sie den folgenden Job, der üblicherweise erklärt werden würde zu "command
ausführen alle 5 Minuten" :
*/5 * * * * /path/to/your/command
gegen:
*/7 * * * * /path/to/your/command
was nicht immer tut Führen Sie command
aus alle 7 Minuten .
Denken Sie daran, dass die / kann verwendet werden, um einen Schritt einzuleiten, aber diese Schritte enden nicht über das Ende einer Reihe hinaus, z. */7
was jede 7. Minute ab den Minuten 0-59
entspricht also 0,7,14,21,28,35,42,49,56 aber zwischen einer Stunde und der nächsten zwischen den Chargen liegen nur 4 Minuten , nach 00:56
eine neue Serie beginnt bei 01:00
, 01:07
usw. (und Batches werden nicht auf 01:03
ausgeführt , 01:10
, 01:17
usw.).
Was stattdessen tun?
Erstellen Sie mehrere Stapel
Erstellen Sie statt eines einzelnen Cron-Jobs mehrere Batches, die zusammen den gewünschten Zeitplan ergeben.
Um beispielsweise alle 40 Minuten einen Batch auszuführen (00:00, 00:40, 01:20, 02:00 usw.), erstellen Sie zwei Batches, einen, der zweimal zu den geraden Stunden ausgeführt wird, und einen zweiten, der nur zu den ungeraden Stunden ausgeführt wird:
# The following lines create a batch that runs every 40 minutes i.e.
# runs on 0:00, 0:40, 02:00, 02:40 04:00 etc to 22:40
0,40 */2 * * * /path/to/your/command
# runs on 01:20, 03:20, etc to 23:20
20 1/2 * * * /path/to/your/command
# Combined: 0:00, 0:40, 01:20, 02:00, 02:40, 03:20, 04:00 etc.
Lassen Sie Ihre Batches seltener laufen
Anstatt Ihren Batch alle 7 Minuten auszuführen, was ein schwieriger Zeitplan ist, um ihn in mehrere Batches aufzuteilen, führen Sie ihn stattdessen einfach alle 10 Minuten aus.
Starten Sie Ihre Chargen häufiger (aber verhindern, dass mehrere Batches gleichzeitig ausgeführt werden)
Viele ungerade Zeitpläne entwickeln sich, weil die Batch-Laufzeiten zunehmen/schwanken und die Batches dann mit einem kleinen zusätzlichen Sicherheitsspielraum geplant werden, um zu verhindern, dass nachfolgende Läufe desselben Batches sich überschneiden und gleichzeitig ausgeführt werden.
Denken Sie stattdessen anders und erstellen Sie einen Cronjob, der ordnungsgemäß fehlschlägt, wenn ein vorheriger Lauf noch nicht beendet ist, der aber ansonsten ausgeführt wird. Siehe diese Fragen und Antworten:
* * * * * /usr/bin/flock -n /tmp/fcj.lockfile /usr/local/bin/frequent_cron_job
Dadurch wird fast sofort ein neuer Lauf gestartet, sobald der vorherige Lauf von /usr/local/bin/frequent_cron_job abgeschlossen ist.
Starten Sie Ihre Chargen häufiger (aber elegant beenden, wenn die Bedingungen nicht stimmen)
Da die Cron-Syntax begrenzt ist, können Sie entscheiden, komplexere Bedingungen und Logik in den Batch-Job selbst zu platzieren (oder in einem Wrapper-Skript um den vorhandenen Batch-Job). Dadurch können Sie die erweiterten Fähigkeiten Ihrer bevorzugten Skriptsprachen nutzen, Ihren Code kommentieren und schwer lesbare Konstrukte im crontab-Eintrag selbst verhindern.
In bash der seven-minute-job
würde dann in etwa so aussehen:
#!/bin/bash
# seven-minute-job
# This batch will only run when 420 seconds (7 min) have passed
# since the file /tmp/lastrun was either created or updated
if [ ! -f /tmp/lastrun ] ; then
touch /tmp/lastrun
fi
if [ $(( $(date +%s) - $(date -r /tmp/lastrun +%s) )) -lt 420 ] ; then
# The minimum interval of 7 minutes between successive batches hasn't passed yet.
exit 0
fi
#### Start running your actual batch job below
/path/to/your/command
#### actual batch job is done, now update the time stamp
date > /tmp/lastrun
#EOF
Was Sie dann sicher jede Minute ausführen (versuchen) können:
* * * * * /path/to/your/seven-minute-job
Ein anderes, aber ähnliches Problem wäre, einen Batch so zu planen, dass er am ersten Montag jedes Monats (oder am zweiten Mittwoch) usw. ausgeführt wird. Planen Sie einfach, dass der Batch jeden Montag ausgeführt und beendet wird, wenn das Datum weder zwischen 1 noch 7 und dem Tag liegt der Woche ist nicht der Montag.
#!/bin/bash
# first-monday-of-the-month-housekeeping-job
# exit if today is not a Monday (and prevent locale issues by using the day number)
if [ $(date +%u) != 1 ] ; then
exit 0
fi
# exit if today is not the first Monday
if [ $(date +%d) -gt 7 ] ; then
exit 0
fi
#### Start running your actual batch job below
/path/to/your/command
#EOF
Was Sie dann sicher jeden Montag ausführen (versuchen) können:
0 0 * * 1 /path/to/your/first-monday-of-the-month-housekeeping-job
Cron nicht verwenden
Wenn Ihre Anforderungen komplex sind, sollten Sie ein fortschrittlicheres Produkt verwenden, das für die Ausführung komplexer Zeitpläne (auf mehrere Server verteilt) ausgelegt ist und Trigger, Jobabhängigkeiten, Fehlerbehandlung, Wiederholungen und Wiederholungsüberwachung usw. unterstützt. Der Branchenjargon wäre „Enterprise " Job Scheduling und/oder "Workload Automation".
Lösung 5:
PHP-spezifisch
Wenn Sie einen Cron-Job haben wie:
php /bla/bla/something.php >> /var/logs/somelog-for-stdout.log
Und im Falle von Fehlern erwarten Sie, dass sie Ihnen zugesandt werden, aber sie nicht -- überprüfen Sie dies.
PHP sendet standardmäßig keine Fehler an STDOUT. @siehe https://bugs.php.net/bug.php?id=22839
Um dies zu beheben, fügen Sie Folgendes in cli`s php.ini oder in Ihrer Zeile (oder in Ihrem Bash-Wrapper für PHP) hinzu:
- --define display_startup_errors=1
- --define display_errors='stderr'
Die 1. Einstellung ermöglicht es Ihnen, fatale Meldungen wie „Memory oops“ zu haben und die 2. – sie alle auf STDERR umzuleiten. Erst danach können Sie gut schlafen, da alles an die Mail Ihres Roots gesendet wird, anstatt nur protokolliert zu werden.