Aller au contenu
Allin

Comment écrire une expression cron : cinq champs et la règle du OU dont personne ne parle

Publié le 21/05/2026 · 10 min de lecture · Outils pour développeurs

Daniel Okonkwo

Daniel OkonkwoDéveloppeur front-end et rédacteur Tech chez Allin

Performance web · Formats de fichiers

Vérifié à partir de 2 sources

Voir le profil
En bref

Une expression cron, ce sont cinq champs séparés par des espaces, lus de gauche à droite : minute (0-59), heure (0-23), jour du mois (1-31), mois (1-12 ou noms de trois lettres) et jour de la semaine (0-6 en POSIX avec 0 pour dimanche, étendu à 0-7 par la plupart des implémentations de sorte que 7 vaut aussi dimanche). Un astérisque désigne toutes les valeurs de la plage du champ. Un tiret forme une plage inclusive : 8-11 dans le champ heure vaut 8, 9, 10 et 11. Une virgule forme une liste, et listes et plages se mélangent : 1-3,7-9. Une barre oblique ajoute un pas, et c'est là que ça dérape la première fois — un pas parcourt une plage, il ne fixe pas un intervalle : */15 dans le champ minute vaut donc 0, 15, 30 et 45, et */40 vaut 0 et 40 puis plus rien jusqu'à ce que l'heure suivante relance la plage. Le second piège, plus grave, est que le jour du mois et le jour de la semaine se combinent par OU et non par ET. Si les deux sont restreints, la tâche s'exécute dès que l'un des deux correspond. Ainsi 0 0 1 * 1 ne signifie pas le premier lundi du mois : cela se déclenche à minuit le 1er de chaque mois ET à minuit tous les lundis. Pour fixer une tâche à un jour de semaine précis, laisse un astérisque au jour du mois, et inversement. Tout le reste que tu croiseras — @daily, un sixième champ de secondes, le caractère ?, L, W et # — sort du standard et dépend entièrement du cron que tu exécutes.

Minute, heure, jour du mois, mois, jour de la semaine. Les pièges : un pas est une progression dans une plage et non un intervalle, et les deux champs de jour sont combinés par OU — si bien que 0 0 1 * 1 se déclenche le 1er et tous les lundis.

Cinq champs, et un pas n'est pas un intervalle

Lis les champs de gauche à droite et la notation tient dans la tête. L'astérisque représente toute la plage de son champ. Le tiret forme une plage inclusive : 8-11 dans le champ heure fait quatre heures, pas trois. La virgule forme une liste, et listes et plages se combinent librement dans le même champ : 1-3,7-9 est un ensemble légal de six valeurs. Les noms peuvent remplacer les nombres dans les champs mois et jour de la semaine, sur trois lettres, mais le cron Vixie classique n'accepte ni plage ni liste construite à partir de noms — 1-3 passe là où JAN-MAR peut échouer, bonne raison de s'en tenir aux nombres dans tout ce qui doit être portable.

La barre oblique est l'endroit où le modèle mental casse. Un pas parcourt une plage, ce n'est pas un intervalle de répétition, et la plage repart au début de chaque unité parente. Dans le champ minute, */15 parcourt 0, 15, 30, 45, puis l'heure bascule et il repart à 0 — ce qui revient justement à toutes les quinze minutes, personne ne remarque donc la nuance. Écris */40 et l'illusion s'effondre : il parcourt 0 et 40, puis l'heure bascule et il repart à 0, donnant des écarts de quarante minutes puis de vingt. Idem pour une plage bornée : 0-23/2 dans le champ heure donne les heures paires, et 1-9/2 donne 1, 3, 5, 7 et 9. Si tu as réellement besoin d'un intervalle qui ne divise pas son unité, cron ne peut pas l'exprimer en une ligne : il te faut deux entrées ou une garde à l'intérieur de la commande.

Les deux champs de jour se combinent par OU, pas par ET

C'est la règle que la plupart des explications ratent, et ce n'est pas une bizarrerie d'une implémentation : c'est écrit dans la spécification. POSIX énonce que si le jour du mois est donné comme élément ou liste et que le jour de la semaine l'est aussi, alors tout jour correspondant soit au jour du mois soit au jour de la semaine est retenu. Le manuel crontab(5) dit la même chose plus simplement et donne l'exemple canonique : 30 4 1,15 * 5 s'exécute à 4 h 30 du matin le 1er et le 15 de chaque mois, plus tous les vendredis. Pas le 1er ou le 15 lorsqu'ils tombent un vendredi. Les deux ensembles, réunis.

Les règles pratiques qui en découlent sont brèves. Si exactement un des deux champs de jour est un astérisque, l'autre gouverne et il n'y a pas d'ambiguïté — c'est le cas qu'il faut écrire presque à chaque fois. Si les deux sont des astérisques, tous les jours correspondent, ce qui est également sans ambiguïté. C'est seulement quand les deux sont restreints que l'union s'applique, et le résultat n'est presque jamais celui voulu : 0 0 1 * 1 se déclenche environ cinq fois par mois au lieu d'une. Il n'existe pas de syntaxe cron pour l'intersection : si tu as vraiment besoin du premier lundi du mois, l'astuce standard consiste à programmer l'union puis à la restreindre dans la commande — lancer sur 0 0 1-7 * 1 en ouvrant le script par un test vérifiant que le jour de la semaine est lundi, ou sur 0 0 * * 1 avec un test vérifiant que le jour du mois vaut 7 au plus. Les crons de style Quartz ajoutent un opérateur # pour cela, mais il n'est pas disponible dans le cron système.

Tout ce qui dépasse les cinq champs dépend de l'implémentation

POSIX définit cinq champs, l'astérisque, les plages, les listes, et rien d'autre. Chaque commodité vue au-delà est une extension, et les extensions varient. Le pas avec barre oblique est une extension Vixie aujourd'hui quasi universelle mais toujours absente du standard. Les chaînes raccourcies — @yearly et son synonyme @annually, @monthly, @weekly, @daily et son synonyme @midnight, @hourly, et @reboot — viennent de la même lignée, et @reboot en particulier n'a pas de sens fixe d'un système à l'autre, car ce qui compte comme redémarrage dépend du démon. Un sixième champ pour les secondes est courant dans les ordonnanceurs applicatifs comme Quartz, Spring et plusieurs bibliothèques Node, et absent du cron système : une expression copiée depuis la documentation d'un framework vers un crontab sera décalée d'un champ et programmera tout autre chose au lieu d'échouer bruyamment.

Le point d'interrogation appartient à la même famille. Dans Quartz, il signifie aucune valeur spécifique et existe précisément pour lever l'ambiguïté entre jour du mois et jour de la semaine en déclarant un champ non pertinent ; le cron système ne l'accepte pas du tout. Les opérateurs L, W et # pour dernier jour du mois, jour ouvré le plus proche et énième jour de semaine sont eux aussi propres à Quartz. Deux faits d'environnement comptent plus que toute cette syntaxe : le démon exécute les tâches dans le fuseau horaire pour lequel il est configuré, si bien qu'une expression correcte dans un déploiement peut se déclencher une heure trop tôt ou trop tard dans un autre, et lors d'un changement d'heure une tâche programmée dans l'heure supprimée peut ne pas s'exécuter du tout tandis qu'une tâche dans l'heure répétée peut s'exécuter deux fois. Et cron est un déclencheur, pas un exécuteur de tâches — pas de reprise, pas de contrôle de concurrence, aucune mémoire d'un échec précédent. Si deux invocations ne doivent pas se chevaucher, prends toi-même un verrou dans la commande.

Les cinq champs, dans l'ordre, avec l'erreur que chacun invite
PositionChampValeurs autoriséesL'erreur qu'il invite
1erMinute0-59Écrire * ici alors qu'on voulait 0 — une expression comme * 3 * * * s'exécute soixante fois, chaque minute de l'heure de 3 h
2eHeure0-23Raisonner en horloge de 12 heures : il n'y a ni 24 ni après-midi, minuit vaut 0 et 23 h vaut 23
3eJour du mois1-31Utiliser 31 en attendant douze exécutions par an : les mois plus courts ne correspondent tout simplement jamais, la tâche saute donc en silence février, avril, juin, septembre et novembre
4eMois1-12, ou JAN à DECCroire que les noms passent partout : le cron Vixie classique accepte les noms de trois lettres mais refuse leurs plages ou listes, si bien que JAN-MAR peut échouer là où 1-3 passe
5eJour de la semaine0-6 en POSIX avec 0 pour dimanche ; 0-7 dans la plupart des implémentations, où 7 vaut aussi dimancheRestreindre ce champ et le jour du mois en même temps : les deux sont combinés par OU, la tâche se déclenche donc sur les deux ensembles de jours et non sur leur intersection
Générateur d'expression cronConstruis une planification cron champ par champ — minute, heure, jour du mois, mois, jour de semaine — et vois l'expression et sa description en clair se mettre à jour en direct. Des préréglages couvrent les cas courants, et la syntaxe (*, */5, 1-5, 1,15) est expliquée.Essayer l'outil

Questions fréquentes

Que signifie exactement */15 ?
Dans le champ minute, il sélectionne les valeurs 0, 15, 30 et 45 — un pas de quinze à travers la plage 0 à 59, en partant de la première valeur de la plage. Ce n'est pas un minuteur qui se déclenche quinze minutes après la dernière exécution, et il ne survit pas à une division inexacte : */40 dans le champ minute ne sélectionne que 0 et 40, les écarts font donc quarante minutes puis vingt lorsque l'heure suivante relance la plage. Si tu veux un intervalle mesuré depuis la dernière exécution et non depuis le début de l'heure, cron n'est pas le bon outil : un minuteur systemd avec OnUnitActiveSec, ou une boucle dans un processus de longue durée, l'est.
Comment lancer une tâche toutes les 90 minutes ?
Pas en une seule ligne, car 90 ne divise pas 60 et un pas reste enfermé dans un seul champ. Le motif se répète toutefois toutes les trois heures, si bien que deux entrées le couvrent exactement : 0 */3 * * * donne 00 h 00, 03 h 00, 06 h 00 et ainsi de suite, et 30 1-23/3 * * * donne 01 h 30, 04 h 30, 07 h 30 et ainsi de suite. Ensemble, elles produisent une exécution toutes les quatre-vingt-dix minutes, et la séquence boucle proprement à minuit puisque vingt-quatre heures font exactement seize intervalles de quatre-vingt-dix minutes. La même astuce à deux entrées marche pour tout intervalle qui divise un nombre entier d'heures ; ce qui n'est pas le cas, comme toutes les 50 minutes, ne peut pas s'exprimer en cron du tout.
Pourquoi ma tâche nocturne s'est-elle exécutée deux fois, ou pas du tout ?
La cause habituelle est un changement d'heure. Cron compare l'heure du mur : quand l'horloge avance, une heure qui n'a jamais existé n'est jamais atteinte et une tâche programmée dedans ne s'exécute tout simplement pas ; quand l'horloge recule, une heure survient deux fois et une tâche programmée dedans peut se déclencher deux fois. Les implémentations diffèrent par l'effort de compensation, d'où un même crontab qui se comporte différemment sur deux distributions. Les corrections fiables : programmer les tâches sensibles hors de la fenêtre de transition, faire tourner le démon dans un fuseau sans changement d'heure, ou rendre la commande elle-même sûre à exécuter deux fois. Cette dernière vaut de toute façon : l'idempotence te protège aussi des reprises, des exécutions concurrentes et des relances manuelles.

Articles qui pourraient t'intéresser

Tous les guides
ExplicationComment fonctionnent les permissions Unix : lire 755 sans devinerLecture 4, écriture 2, exécution 1, et chacun des trois chiffres décrit une partie différente. Ce que la plupart des explications ratent : ce que fait le bit d'exécution sur un répertoire — il accorde la traversée, pas le droit de lancer quoi que ce soit.GuideLes codes de statut HTTP expliqués : ceux qu'on confond vraiment301 contre 308, 302 contre 307, 401 contre 403, 404 contre 410 — plus ce que promet réellement Retry-After sur un 429 ou un 503. Les paires où choisir le mauvais code change le comportement, pas seulement la formulation.ExplicationQu'est-ce qu'une expression cron ?Une expression cron planifie l'exécution automatique d'une tâche à des moments définis. Voici à quoi elle sert, ses cinq champs, comment la lire, et les pièges courants.TutorielComment écrire un robots.txt : directives, correspondance, et ce qu'il ne peut pas cacherQuatre directives, deux jokers, un fichier à la racine de l'hôte. C'est une instruction d'exploration et rien de plus — il ne retire pas une page des résultats, ne restreint pas l'accès, et publie chaque chemin que tu y inscrives.ExplicationCe qu'un pourcentage de disponibilité autorise réellementTrois neuf, cela sonne comme une promesse — jusqu'à ce qu'on le divise en minutes. Ce que 99,9 % achète par an, par mois, par semaine et par jour ; pourquoi la fenêtre de mesure compte bien plus que le neuf supplémentaire ; et les deux mois différents que cet outil utilise pour un seul et même identifiant.ExplicationL'heure dorée et l'heure bleue sont des angles, pas des heuresL'heure dorée va de +6° à −4° de hauteur du soleil et l'heure bleue de −4° à −6°, ce pour quoi elle dure quarante minutes à l'équateur, plus d'une heure aux latitudes moyennes, et au-delà de 72,6° en juin n'a pas lieu du tout. Les seuils de ce calculateur, vérifiés sur sa propre sortie.

Outils similaires

Sources

Tu as repéré une erreur dans cet article ?