Solution 1 :
Comment résoudre tous vos problèmes/problèmes liés à la crontab (Linux)
Ceci est un wiki communautaire, si vous remarquez quelque chose d'incorrect avec cette réponse ou si vous avez des informations supplémentaires, veuillez la modifier.
Tout d'abord, la terminologie de base :
- cron(8) est le démon qui exécute les commandes planifiées.
- crontab(1) est le programme utilisé pour modifier les fichiers utilisateur crontab(5).
- crontab(5) est un fichier par utilisateur qui contient des instructions pour cron(8).
Ensuite, formation sur cron :
Chaque utilisateur d'un système peut avoir son propre fichier crontab. L'emplacement des fichiers crontab racine et utilisateur dépend du système, mais ils sont généralement inférieurs à /var/spool/cron
.
Il existe un /etc/crontab
à l'échelle du système fichier, le /etc/cron.d
Le répertoire peut contenir des fragments de crontab qui sont également lus et traités par cron. Certaines distributions Linux (par exemple, Red Hat) ont également /etc/cron.{hourly,daily,weekly,monthly}
qui sont des répertoires, des scripts à l'intérieur desquels seront exécutés chaque heure/jour/semaine/mois, avec le privilège root.
root peut toujours utiliser la commande crontab ; les utilisateurs réguliers peuvent ou non se voir accorder l'accès. Lorsque vous éditez le fichier crontab avec la commande crontab -e
et enregistrez-le, crond vérifie sa validité de base mais ne garantit pas que votre fichier crontab est correctement formé. Il existe un fichier nommé cron.deny
qui précisera quels utilisateurs ne peuvent pas utiliser cron. Le cron.deny
l'emplacement du fichier dépend du système et peut être supprimé, ce qui permettra à tous les utilisateurs d'utiliser cron.
Si l'ordinateur n'est pas sous tension ou si le démon crond n'est pas en cours d'exécution et que la date/l'heure d'exécution d'une commande est passée, crond ne rattrapera pas et n'exécutera pas les requêtes passées.
détails crontab, comment formuler une commande :
Une commande crontab est représentée par une seule ligne. Vous ne pouvez pas utiliser \
pour étendre une commande sur plusieurs lignes. Le hachage (#
) représente un commentaire qui signifie que tout ce qui se trouve sur cette ligne est ignoré par cron. Les espaces et les lignes vides en début de ligne sont ignorés.
Soyez TRÈS prudent lorsque vous utilisez le pourcentage (%
) connectez-vous à votre commande. Sauf s'ils sont échappés \%
ils sont convertis en nouvelles lignes et tout après le premier %
non échappé est passé à votre commande sur stdin.
Il existe deux formats pour les fichiers crontab :
-
Crontabs utilisateur
# 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
-
À l'échelle du système
/etc/crontab
et/etc/cron.d
fragments# 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
Notez que ce dernier nécessite un nom d'utilisateur. La commande sera exécutée en tant qu'utilisateur nommé.
Les 5 premiers champs de la ligne représentent l'heure ou les heures auxquelles la commande doit être exécutée. Vous pouvez utiliser des nombres ou, le cas échéant, des noms de jour/mois dans la spécification de l'heure.
- Les champs sont séparés par des espaces ou des tabulations.
- Une virgule (
,
) est utilisé pour spécifier une liste, par exemple 1,4,6,8, ce qui signifie exécuter à 1,4,6,8. - Les plages sont spécifiées avec un tiret (
-
) et peuvent être combinés avec des listes, par ex. 1-3,9-12 qui signifie entre 1 et 3 puis entre 9 et 12. - Le
/
Le caractère peut être utilisé pour introduire une étape, par ex. 2/5 c'est à dire à partir de 2 puis tous les 5 (2,7,12,17,22...). Ils ne dépassent pas la fin. - Un astérisque (
*
) dans un champ signifie la plage entière pour ce champ (par exemple0-59
pour le champ des minutes). - Les plages et les étapes peuvent être combinées, par ex.
*/2
signifie commencer au minimum pour le champ concerné puis tous les 2 par ex. 0 pour les minutes (0,2...58), 1 pour les mois (1,3...11) etc.
Débogage des commandes cron
Vérifiez le courrier !
Par défaut, cron enverra toute sortie de la commande à l'utilisateur sous lequel il exécute la commande. S'il n'y a pas de sortie, il n'y aura pas de courrier. Si vous voulez que cron envoie du courrier à un compte différent, vous pouvez définir la variable d'environnement MAILTO dans le fichier crontab, par exemple
[email protected]
1 2 * * * /path/to/your/command
Capturez la sortie vous-même
Vous pouvez rediriger stdout et stderr vers un fichier. La syntaxe exacte pour capturer la sortie peut varier en fonction du shell cron utilisé. Voici deux exemples qui enregistrent toutes les sorties dans un fichier à /tmp/mycommand.log
:
1 2 * * * /path/to/your/command &>/tmp/mycommand.log
1 2 * * * /path/to/your/command >/tmp/mycommand.log 2>&1
Consultez les journaux
Cron enregistre ses actions via syslog, qui (selon votre configuration) va souvent à /var/log/cron
ou /var/log/syslog
.
Si nécessaire, vous pouvez filtrer les instructions cron avec par exemple
grep CRON /var/log/syslog
Maintenant que nous avons passé en revue les bases de cron, où se trouvent les fichiers et comment les utiliser, examinons quelques problèmes courants.
Vérifiez que cron est en cours d'exécution
Si cron ne s'exécute pas, vos commandes ne seront pas planifiées ...
ps -ef | grep cron | grep -v grep
devrait vous donner quelque chose comme
root 1224 1 0 Nov16 ? 00:00:03 cron
ou
root 2018 1 0 Nov14 ? 00:00:06 crond
Sinon, redémarrez-le
/sbin/service cron start
ou
/sbin/service crond start
Il peut y avoir d'autres méthodes; utilisez ce que votre distribution fournit.
cron exécute votre commande dans un environnement restreint.
Les variables d'environnement disponibles sont probablement très limitées. En règle générale, vous n'obtiendrez que quelques variables définies, telles que $LOGNAME
, $HOME
, et $PATH
.
Il convient de noter en particulier le PATH
est limité à /bin:/usr/bin
. La grande majorité des problèmes "mon script cron ne fonctionne pas" sont causés par ce chemin restrictif . Si votre commande se trouve à un emplacement différent, vous pouvez résoudre ce problème de plusieurs manières :
-
Indiquez le chemin complet de votre commande.
1 2 * * * /path/to/your/command
-
Fournissez un PATH approprié dans le fichier crontab
PATH=/bin:/usr/bin:/path/to/something/else 1 2 * * * command
Si votre commande nécessite d'autres variables d'environnement, vous pouvez également les définir dans le fichier crontab.
cron exécute votre commande avec cwd ==$HOME
Quel que soit l'endroit où le programme que vous exécutez réside sur le système de fichiers, le répertoire de travail actuel du programme lorsque cron l'exécute sera le répertoire personnel de l'utilisateur . Si vous accédez à des fichiers dans votre programme, vous devrez en tenir compte si vous utilisez des chemins relatifs, ou (de préférence) utilisez simplement des chemins entièrement qualifiés partout, et évitez à tout le monde beaucoup de confusion.
La dernière commande de ma crontab ne s'exécute pas
Cron exige généralement que les commandes se terminent par une nouvelle ligne. Modifiez votre crontab ; allez à la fin de la ligne qui contient la dernière commande et insérez une nouvelle ligne (appuyez sur entrée).
Vérifier le format crontab
Vous ne pouvez pas utiliser un crontab utilisateur formaté par crontab pour /etc/crontab ou les fragments dans /etc/cron.d et vice versa. Une crontab formatée par l'utilisateur n'inclut pas de nom d'utilisateur en 6ème position d'une ligne, tandis qu'une crontab formatée par le système inclut le nom d'utilisateur et exécute la commande en tant que cet utilisateur.
J'ai mis un fichier dans /etc/cron.{hourly,daily,weekly,monthly} et il ne s'exécute pas
- Vérifiez que le nom du fichier n'a pas d'extension voir run-parts
- Assurez-vous que le fichier dispose des autorisations d'exécution.
- Indiquez au système ce qu'il faut utiliser lors de l'exécution de votre script (par exemple, mettre
#!/bin/sh
en haut)
Bogues liés à la date de Cron
Si votre date est récemment modifiée par un utilisateur ou une mise à jour du système, un fuseau horaire ou autre, alors crontab commencera à se comporter de manière erratique et présentera des bogues bizarres, parfois fonctionnels, parfois non. C'est la tentative de crontab d'essayer de "faire ce que vous voulez" lorsque l'heure change par en dessous. Le champ "minute" deviendra inopérant après le changement d'heure. Dans ce scénario, seuls les astérisques seraient acceptés. Redémarrez cron et réessayez sans vous connecter à Internet (afin que la date n'ait pas la possibilité de se réinitialiser sur l'un des serveurs de temps).
Signes de pourcentage, encore une fois
Pour mettre l'accent sur les conseils concernant les signes de pourcentage, voici un exemple de ce que cron en fait :
# cron entry
* * * * * cat >$HOME/cron.out%foo%bar%baz
créera le fichier ~/cron.out contenant les 3 lignes
foo
bar
baz
Ceci est particulièrement intrusif lors de l'utilisation du date
commande. Assurez-vous d'échapper aux signes de pourcentage
* * * * * /path/to/command --day "$(date "+\%Y\%m\%d")"
Méfiez-vous du Sudo
lors de l'exécution en tant qu'utilisateur non root,
crontab -e
ouvrira la crontab de l'utilisateur, tandis que
sudo crontab -e
ouvrira la crontab de l'utilisateur root. Il n'est pas recommandé d'exécuter des commandes sudo dans une tâche cron, donc si vous essayez d'exécuter une commande sudo dans le cron d'un utilisateur, essayez de déplacer cette commande vers le cron root et supprimez sudo de la commande.
Solution 2 :
Debian Linux et ses dérivés (Ubuntu, Mint, etc.) présentent certaines particularités qui peuvent empêcher l'exécution de vos tâches cron ; en particulier, les fichiers en /etc/cron.d
, /etc/cron.{hourly,daily,weekly,monthly}
doit :
- être la propriété de root
- uniquement accessible en écriture par root
- ne pas être accessible en écriture par le groupe ou d'autres utilisateurs
- avoir un nom sans point '.' ou tout autre caractère spécial sauf '-' et '_' .
Le dernier blesse régulièrement les utilisateurs peu méfiants; en particulier tout script dans l'un de ces dossiers nommé whatever.sh
, mycron.py
, testfile.pl
, etc. ne seront pas être exécuté, jamais.
D'après mon expérience, ce point particulier a été de loin la raison la plus fréquente d'un cronjob non exécuté sur Debian et ses dérivés.
Voir man cron
pour plus de détails, si nécessaire.
Solution 3 :
Si vos tâches cron cessent de fonctionner, vérifiez que votre mot de passe n'a pas expiré, car une fois qu'il a expiré, toutes les tâches cron s'arrêtent.
Il y aura des messages en /var/log/messages
similaire à celui ci-dessous qui montre des problèmes d'authentification de l'utilisateur :
(username) FAILED to authorize user with PAM (Authentication token is no longer valid; new one required)
Solution 4 :
Horaires inhabituels et irréguliers
Cron est tout considéré comme un planificateur très basique et la syntaxe ne permet pas facilement à un administrateur de formuler des planifications légèrement plus inhabituelles.
Considérez le travail suivant qui serait généralement expliqué à "exécuter command
toutes les 5 minutes" :
*/5 * * * * /path/to/your/command
contre :
*/7 * * * * /path/to/your/command
qui ne fait pas toujours exécutez command
toutes les 7 minutes .
Rappelez-vous que le / Le caractère peut être utilisé pour introduire une étape, mais ces étapes ne s'étendent pas au-delà de la fin d'une série, par ex. */7
qui correspond toutes les 7 minutes à partir des minutes 0-59
c'est-à-dire 0,7,14,21,28,35,42,49,56 mais entre une heure et la suivante il y aura seulement 4 minutes entre les lots , après 00:56
une nouvelle série commence à 01:00
, 01:07
etc. (et les lots ne fonctionneront pas sur 01:03
, 01:10
, 01:17
etc.).
Que faire à la place ?
Créer plusieurs lots
Plutôt qu'une seule tâche cron, créez plusieurs lots qui, combinés, donnent le calendrier souhaité.
Par exemple, pour exécuter un batch toutes les 40 minutes (00:00, 00:40, 01:20, 02:00 etc.) créez deux batchs, un qui s'exécute deux fois les heures paires et le second qui ne s'exécute que les heures impaires :
# 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.
Exécutez vos lots moins fréquemment
Plutôt que d'exécuter votre lot toutes les 7 minutes, ce qui est un calendrier difficile à décomposer en plusieurs lots, exécutez-le simplement toutes les 10 minutes à la place.
Démarrez vos lots plus fréquemment (mais empêcher plusieurs lots de s'exécuter simultanément)
De nombreux horaires impairs évoluent parce que les durées d'exécution des lots augmentent/fluctuent, puis les lots sont planifiés avec un peu de marge de sécurité supplémentaire pour éviter que les exécutions ultérieures du même lot ne se chevauchent et ne s'exécutent simultanément.
Au lieu de cela, pensez différemment et créez une tâche cron qui échouera correctement lorsqu'une exécution précédente n'est pas encore terminée, mais qui s'exécutera autrement. Voir cette FAQ :
* * * * * /usr/bin/flock -n /tmp/fcj.lockfile /usr/local/bin/frequent_cron_job
Cela lancera presque immédiatement une nouvelle exécution une fois l'exécution précédente de /usr/local/bin/frequent_cron_job terminée.
Démarrez vos lots plus fréquemment (mais quittez gracieusement lorsque les conditions ne sont pas réunies)
Étant donné que la syntaxe cron est limitée, vous pouvez décider de placer des conditions et une logique plus complexes dans le travail par lots lui-même (ou dans un script wrapper autour du travail par lots existant). Cela vous permet d'utiliser les fonctionnalités avancées de vos langages de script préférés, de commenter votre code et d'éviter les constructions difficiles à lire dans l'entrée crontab elle-même.
En bash le seven-minute-job
ressemblerait alors à quelque chose comme :
#!/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
Que vous pouvez ensuite (essayer) d'exécuter en toute sécurité toutes les minutes :
* * * * * /path/to/your/seven-minute-job
Un problème différent, mais similaire, serait de programmer un lot pour qu'il s'exécute le premier lundi de chaque mois (ou le deuxième mercredi), etc. Il suffit de programmer le lot pour qu'il s'exécute tous les lundis et quittez lorsque la date n'est ni entre le 1 ou le 7 et le jour de la semaine n'est pas lundi.
#!/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
Que vous pouvez ensuite (essayer) d'exécuter en toute sécurité tous les lundis :
0 0 * * 1 /path/to/your/first-monday-of-the-month-housekeeping-job
N'utilisez pas cron
Si vos besoins sont complexes, vous pouvez envisager d'utiliser un produit plus avancé conçu pour exécuter des planifications complexes (distribuées sur plusieurs serveurs) et qui prend en charge les déclencheurs, les dépendances des tâches, la gestion des erreurs, les tentatives et la surveillance des nouvelles tentatives, etc. " planification des tâches et/ou " automatisation de la charge de travail ".
Solution 5 :
Spécifique à PHP
Si vous avez une tâche cron comme :
php /bla/bla/something.php >> /var/logs/somelog-for-stdout.log
Et en cas d'erreurs, attendez-vous à ce qu'elles vous soient envoyées, mais ce n'est pas le cas -- cochez ceci.
PHP par défaut n'envoie pas d'erreurs à STDOUT. @voir https://bugs.php.net/bug.php?id=22839
Pour résoudre ce problème, ajoutez dans cli`s php.ini ou dans votre ligne (ou dans votre wrapper bash pour PHP) ceci :
- --define display_startup_errors=1
- --define display_errors='stderr'
Le 1er paramètre vous permettra d'avoir des fatals comme 'Memory oops' et le 2ème -- de les rediriger tous vers STDERR. Ce n'est qu'une fois que vous pourrez bien dormir que tout sera envoyé au courrier de votre racine au lieu d'être simplement connecté.