Planification de tâches sous Linux

Qu'est-ce que cron?

cron est un démon (service en arrière-plan) sous Linux qui exécute des tâches planifiées à des intervalles réguliers. Le nom vient du grec "chronos" (temps).

Composants du système cron

  1. Le démon crond : Service système qui s'exécute en permanence
  2. La crontab : Fichier de configuration contenant les tâches planifiées
  3. Les logs : Enregistrent l'exécution des tâches

Il existe quelques alternatives à cron, comme systemd timers ou at, mais cron reste le plus utilisé pour les tâches récurrentes, pour sa simplicité et sa compatibilité avec la plupart des distributions Linux.

La syntaxe crontab

crontab est l'outil pour éditer les fichiers de tâches planifiées. Chaque ligne dans une crontab représente une tâche avec une syntaxe spécifique.

On peut éditer la crontab avec la commande:

crontab -e

Cela ouvre l'éditeur de texte par défaut (souvent nano ou vi) et nous permet d'ajouter, modifier ou supprimer des tâches.

Format de base

Pour chaque tâche à planifier, il faut spécifier 5 champs de temps suivis de la commande à exécuter. Les champs de temps ont une structure fixe:

┌──────── Minute (0-59)
│ ┌────── Heure (0-23)
│ │ ┌──── Jour du mois (1-31)
│ │ │ ┌── Mois (1-12)
│ │ │ │ ┌─ Jour de la semaine (0-7, 0 et 7 = dimanche)
│ │ │ │ │
│ │ │ │ │
* * * * * commande à exécuter

Pour chaque emplacement (minute, heure, jour, mois, jour de la semaine), vous pouvez utiliser soit un chiffre (ex: 5 pour la 5ème minute), un caractère spécial, comme illustré dans le tableau ci-dessous.

Opérateurs spéciaux

Opérateur Signification Exemple Résultat
* Tous * * * * * Chaque minute
, Liste 0,30 * * * * À 0 et 30 minutes de chaque heure
- Plage 0 9-17 * * * Toutes les heures de 9h à 17h
/ Intervalle */15 * * * * Toutes les 15 minutes

Quelques exmples:

Expression Signification
0 0 * * * Tous les jours à minuit
0 12 * * 1-5 Tous les jours de semaine à midi
*/10 * * * * Toutes les 10 minutes
0 0 1 * * Le 1er de chaque mois à minuit
30 2 * * * Tous les jours à 2h30 du matin

Syntaxes spéciales

Il est également possible de remplacer les 5 champs par des mots-clés spéciaux:

Syntaxe Équivalent Utilisation
@reboot - Au démarrage du système
@yearly 0 0 1 1 * Une fois par an (1er janvier à minuit)
@monthly 0 0 1 * * Une fois par mois (le 1er à minuit)
@weekly 0 0 * * 0 Une fois par semaine (dimanche à minuit)
@daily 0 0 * * * Une fois par jour (à minuit)
@hourly 0 * * * * Chaque heure (à la minute 0)

Ainsi, la ligne suivante exécute /home/user/backup.sh chaque jour à minuit:

@daily /home/user/backup.sh

et est équivalente à:

0 0 * * * /home/user/backup.sh

Types de crontab

Il existe deux principaux types de crontab sous Linux: le crontab utilisateur et la crontab système. Le crontab utilisateur est spécifique à chaque utilisateur, tandis que la crontab système est globale. La principale différence est que lorsqu'une tâche est définie dans la crontab utilisateur, elle s'exécute avec les permissions de cet utilisateur, et quand elle est définie dans la crontab système, elle peut s'exécuter avec les permissions de n'importe quel utilisateur spécifié.

Crontab utilisateur

Comme mentionné précédemment, la commande pour éditer la crontab utilisateur est:

crontab -e

Cette commande a pour effet de modifier le fichier de crontab de l'utilisateur qui est situé dans /var/spool/cron/crontabs/nom_utilisateur. Les tâches définies ici s'exécutent avec les permissions de cet utilisateur, et il n'est pas nécessaire d'utiliser sudo pour les éditer.

Commandes courantes :

  • crontab -e : Éditer la crontab
  • crontab -l : Lister les tâches
  • crontab -r : Supprimer toutes les tâches
  • crontab -u utilisateur -l : Voir la crontab d'un autre utilisateur (sudo requis)

Crontab système

Le crontab système ne se modifie pas par la commande crontab -e. Pour ajouter une tâche système, on édite le fichier /etc/crontab, on ajoute des fichiers dans /etc/cron.d/ ou on place des scripts dans les répertoires /etc/cron.hourly/, /etc/cron.daily/, etc.

Éditer /etc/crontab

Pour ajouter une tâche dans /etc/crontab, on utilise un éditeur de texte avec les droits administrateur, par exemple:

sudo nano /etc/crontab

La syntaxe dans ce fichier est légèrement différente que le crontab utilisateur car elle inclut une colonne supplémentaire pour spécifier l'utilisateur sous lequel la commande doit s'exécuter. La syntaxe est donc:

┌──────── Minute (0-59)
│ ┌────── Heure (0-23)
│ │ ┌──── Jour du mois (1-31)
│ │ │ ┌── Mois (1-12)
│ │ │ │ ┌─ Jour de la semaine (0-7, 0 et 7 = dimanche)
│ │ │ │ │
│ │ │ │ │
* * * * * utilisateur commande à exécuter

Par exemple, pour exécuter un script de maintenance en tant que root tous les jours à 2h du matin, on ajouterait la ligne suivante:

# Exécuter en tant que root tous les jours à 2h
0 2 * * * root /usr/local/bin/maintenance.sh

Un autre exemple pour exécuter un script de nettoyage en tant que www-data toutes les heures:

# Exécuter en tant que www-data toutes les heures
0 * * * * www-data /var/www/scripts/cleanup.sh

Par défaut, le fichier /etc/crontab contient les trois lignes suivantes qui exécutent les scripts dans les répertoires /etc/cron.hourly/, /etc/cron.daily/, etc.:

# m h dom mon dow user  command
17 *    * * *   root    cd / && run-parts --report /etc/cron.hourly
25 6    * * *   root    test -x /usr/sbin/anacron || { cd / && run-parts --report /etc/cron.daily; }
47 6    * * 7   root    test -x /usr/sbin/anacron || { cd / && run-parts --report /etc/cron.weekly; }
52 6    1 * *   root    test -x /usr/sbin/anacron || { cd / && run-parts --report /etc/cron.monthly; }

Ces lignes permettent d'exécuter automatiquement les scripts placés dans ces répertoires à des intervalles réguliers, comme expliqué plus loin.

Utiliser /etc/cron.d/

L'autre méthode pour ajouter des tâches système est de créer des fichiers dans le répertoire /etc/cron.d/. Chaque fichier doit suivre la même syntaxe que /etc/crontab, incluant le champ utilisateur. Par exemple, on pourrait créer un fichier /etc/cron.d/backup avec le contenu suivant:

# Sauvegarde quotidienne en tant que root
0 3 * * * root /usr/local/bin/backup.sh

La différence entre cette méthode et la précédente est principalement organisationnelle. Utiliser /etc/cron.d/ permet de mieux organiser les tâches par service ou application, tandis que /etc/crontab est un fichier unique pour toutes les tâches système.

Utiliser les répertoires cron.

Finalement, nous avons les répertoires /etc/cron.hourly/, /etc/cron.daily/, /etc/cron.weekly/, et /etc/cron.monthly/. Ces répertoires sont utilisés pour exécuter des scripts à des intervalles réguliers sans avoir à modifier la crontab directement.

Répertoire Fréquence d'exécution
/etc/cron.hourly/ Chaque heure
/etc/cron.daily/ Chaque jour
/etc/cron.weekly/ Chaque semaine
/etc/cron.monthly/ Chaque mois

Lorsque vous placez un script dans l'un de ces répertoires, il sera exécuté automatiquement à la fréquence correspondante. Assurez-vous que le script est exécutable (chmod +x script.sh). Le script sera exécuté avec les permissions de l'utilisateur root.

Cette méthode est particulièrement utile pour que les applications ou services puissent ajouter leurs propres tâches planifiées sans modifier les fichiers de crontab.

Environnement d'exécution de cron

Le problème

Lorsqu'un script s'exécute via cron, il s'exécute dans un environnement très différent que s'il était lancé manuellement dans un terminal. Cela peut entraîner des erreurs subtiles, car certaines commandes ou variables d'environnement attendues peuvent ne pas être disponibles. Le tableau suivant illustre les différences principales entre un shell interactif et l'environnement cron.

Aspect Shell interactif Environnement cron
PATH /home/user/bin:/usr/local/bin:/usr/bin:/bin /usr/bin:/bin
Profils chargés .bashrc, .profile Aucun
Variables Nombreuses (DISPLAY, etc.) Minimales (HOME, USER, SHELL)
Shell par défaut Votre shell configuré /bin/sh

Ainsi, un script qui fonctionne manuellement peut échouer avec cron parce que :

  1. Les commandes ne sont pas trouvées (PATH limité)
  2. Les variables d'environnement attendues n'existent pas car le profil n'est pas chargé
  3. Les chemins relatifs ne pointent pas où vous pensez

Ainsi, lors de l'écriture de scripts pour cron, il est crucial de prendre en compte ces différences. Voici quelques solutions courantes.

Utiliser des chemins absolus Pour éviter les problèmes de PATH, utilisez toujours des chemins absolus pour les commandes et fichiers dans vos scripts. Par exemple, au lieu de tar -czf ..., utilisez /usr/bin/tar -czf ....

#!/bin/bash
/usr/bin/tar -czf /home/user/backup.tar.gz /home/user/data

Définir l'environnement dans la crontab À l'intérieur même du fichier crontab, vous pouvez définir des variables d'environnement comme PATH ou SHELL en haut du fichier. Cela fera en sorte que toutes les tâches définies dans cette crontab héritent de ces variables.

PATH=/usr/local/bin:/usr/bin:/bin
SHELL=/bin/bash
MAILTO=admin@example.com

0 2 * * * /home/user/script.sh

Charger l'environnement dans le script Si votre script dépend de variables ou de configurations spécifiques à votre utilisateur, vous pouvez explicitement charger votre profil au début du script. Par exemple, si vous utilisez bash et que vos configurations sont dans ~/.bashrc ou ~/.profile, ajoutez cette ligne au début de votre script:

#!/bin/bash
source ~/.profile
# Maintenant les commandes de votre PATH personnel sont disponibles

Redirection et journalisation

Lorsqu'un script est planifié et exécuté automatiquement, il est crucial d'obtenir des informations sur son exécution: quand le script a été lancé, s'il a réussi ou échoué, et toute sortie ou erreur générée. Ces informations viennent de deux endroits: les sorties de la commande elle-même (stdout et stderr, qui nous indiquerait si quelque chose s'est mal passé) et les logs du système (qui nous diraient si cron a rencontré des problèmes pour lancer la tâche, comme un chemin d'accès manquant).

Nous parlerons donc de la redirection des sorties dans les scripts cron et de la consultation des logs système.

Redirection des sorties dans les scripts cron

Par défaut, cron capture la sortie standard (stdout) et la sortie d'erreur (stderr) de chaque tâche et les envoie par email à l'utilisateur propriétaire de la crontab, à partir du moment où un serveur de messagerie est configuré sur le système. Il est cependant plus pratique de conserver les logs localement. Il faut donc rediriger explicitement ces sorties vers des fichiers de log.

Rappel sur les sorties et la redirection

Les notions sur les sorties et la redirection sous Linux devraient vous être familières, mais voici un bref rappel pour se rafraîchir la mémoire.

En général sous Linux, chaque commande peut produire deux types de sorties:

  • stdout (1) : La sortie standard, utilisée pour les messages normaux
  • stderr (2) : La sortie d'erreur, utilisée pour les messages d'erreur

Par défaut, lorsque nous exécutons une commande dans un terminal, ces deux sorties s'affichent à l'écran, et il n'y a pas de distinction entre elles. Il est possible de rediriger chacune de ces sorties vers des fichiers ou d'autres commandes.

Redirections de base

Syntaxe Description Exemple
> Rediriger stdout vers un fichier (écrase le fichier)
>> Rediriger stdout vers un fichier (ajoute à la fin)
2> Rediriger stderr vers un fichier (écrase le fichier)
2>> Rediriger stderr vers un fichier (ajoute à la fin)
&> Rediriger stdout et stderr vers un fichier (écrase le fichier)
&>> Rediriger stdout et stderr vers un fichier (ajoute à la fin)

Exemples:

# Rediriger stdout vers un fichier
commande > sortie.txt
# Rediriger stderr vers un fichier
commande 2> erreurs.txt
# Rediriger stdout et stderr vers un fichier
commande &> tout.txt
# Rediriger stdout et stderr vers un fichier (ajout)
commande &>> tout.txt

La syntaxe 2>&1 est utilisée pour rediriger stderr vers le même endroit que stdout. Par exemple:

commande > sortie.txt 2>&1

Cela signifie que stdout est redirigé vers sortie.txt, et stderr est redirigé vers le même endroit que stdout, donc également dans sortie.txt.

Finalement, pour ignorer toute sortie, on peut rediriger vers /dev/null, qui est un "trou noir" pour les données:

commande > /dev/null 2>&1

Cela fait disparaître toute sortie, qu'elle soit normale ou d'erreur.

Redirections courantes pour les cron jobs

Maintenant, lorsqu'il est question d'enregistrer les sorties de vos scripts cron, vous pouvez spécifier la redirection directement dans la crontab. Voici quelques exemples courants:

Tout capturer dans un log :

0 2 * * * /script.sh >> /var/log/script.log 2>&1
  • >> : Append (ajouter) à stdout
  • 2>&1 : Rediriger stderr (2) vers stdout (1)

Séparer succès et erreurs :

0 2 * * * /script.sh >> /var/log/success.log 2>> /var/log/errors.log

Ignorer toute sortie :

0 2 * * * /script.sh > /dev/null 2>&1

Ne garder que les erreurs :

0 2 * * * /script.sh > /dev/null 2>> /var/log/errors.log

Logs système de cron

Maintenant que nous avons vu comment capturer les sorties de nos scripts, il est aussi important de savoir où trouver les logs système de cron lui-même. Cron enregistre ses propres activités dans les logs système :

Ubuntu/Debian avec rsyslog : /var/log/syslog

grep CRON /var/log/syslog

CentOS/RHEL avec rsyslog : /var/log/cron

grep CRON /var/log/cron

Distributions utilisant journald :

sudo journalctl -u cron

Ce qui est enregistré :

  • Début d'exécution d'une tâche
  • Fin d'exécution
  • Utilisateur qui a exécuté la tâche
  • Commande exécutée

Ce qui N'est PAS enregistré :

  • La sortie de votre script (stdout/stderr)
  • Le code de retour détaillé

Dépannage

Lorsqu'une tâche cron ne s'exécute pas comme prévu, cela peut être difficile de trouver la source du problème. Il y a plusieurs étapes de dépannage à suivre pour identifier et résoudre le problème.

Checklist de vérification

  1. Le service cron est-il actif ?

    sudo systemctl status cron
    
  2. Le script est-il exécutable ?

    ls -l /chemin/vers/script.sh
    # Devrait montrer -rwxr-xr-x
    
  3. Le script fonctionne-t-il manuellement ?

    /chemin/vers/script.sh
    echo $?  # 0 = succès
    
  4. La syntaxe cron est-elle correcte ?

    • Utilisez des outils en ligne pour valider : crontab.guru
  5. Y a-t-il des erreurs dans les logs ?

    grep CRON /var/log/syslog | grep $(whoami)