Contrôle et reprise d'une automatisation locale : erreur, journal, verrou, alerte, redémarrage
Une automatisation qui tourne ne suffit pas. Pour choisir le bon niveau avant ces garde-fous, partez du cadre général pour commencer une automatisation. Il faut aussi qu’elle s’arrête quand elle doit s’arrêter, qu’elle écrive quelque part quand elle échoue, qu’elle refuse un second lancement quand le premier n’est pas terminé, qu’elle prévienne quand elle n’a rien pu faire, et qu’elle redémarre quand la machine revient. Cette page prolonge *Automatiser une petite routine* en couvrant ces cinq angles, mesurés sur une seule machine Linux.
Sommaire
- Quels sont les trois critères d'arrêt utile ?
- Quel journal minimal écrire ?
- Comment bloquer un second lancement quand le premier tourne ?
- Comment alerter quand quelque chose ne va pas ?
- Comment redémarrer automatiquement après une coupure ?
- Comment tester qu'un échec produit bien une alerte ?
- Comment prouver que la routine est idempotente ?
- Quels sont les angles que cette méthode ne couvre pas ?
- Repères pour aller plus loin

Mesures datées : 2026-09-15, machine Linux 6.8.0-106-generic, bash 5.2.21, systemd 255 (255.4-1ubuntu8.17), flock d’util-linux 2.39.3, Python 3.12.3. Aucun paquet ajouté, aucune dépendance externe.
Quels sont les trois critères d'arrêt utile ?
Une routine qui n’a aucun critère d’arrêt est une routine qui décide elle-même de ce qu’elle fait. Trois catégories suffisent à encadrer les défaillances habituelles, et chacune se vérifie en une ligne.
Catégorie 1, code de retour non nul. Toute commande Unix qui échoue sort avec un code non nul ; ce code se consulte avec echo $?. Une routine qui appelle une commande externe doit propager ce code, sinon elle réussit syntaxiquement alors que l’étape a échoué.
ls /chemin/inexistant; echo "rc=$?"
ls: cannot access '/chemin/inexistant': No such file or directory
rc=2
Catégorie 2, absence de production. Une routine qui s’exécute sans erreur peut n’avoir rien fait : un dossier vide, un filtre qui exclut tout, un téléchargement qui a renvoyé un fichier vide. Le critère vérifiable est un compte : combien d’éléments ont été reçus, combien ont été transformés, combien ont été écrits. Une différence entre « reçus » et « écrits » qui n’est pas documentée est un trou.
shopt -s nullglob; f=(*.txt); echo "fichiers trouves: ${#f[@]}"
fichiers trouves: 0
Catégorie 3, dépassement d’un seuil mesurable. Espace disque, durée, taille d’un fichier, nombre d’erreurs dans une fenêtre de temps : un seuil chiffré transforme une impression en signal. La mesure doit précéder l’action, pas la suivre.
df -P / | awk 'NR==2 {gsub(/%/,"",$5); print "seuil teste 80% -> valeur mesuree",$5"%"; exit ($5+0 >= 80)}'
echo "rc=$?"
seuil teste 80% -> valeur mesuree 79%
rc=0
Les trois catégories se combinent. Une alerte disque qui sort en rc=0 quand le seuil n’est pas franchi est correcte. Une alerte disque qui sort en rc=0 parce qu’elle n’a pas pu lire /proc/diskstats est une autre affaire : le critère 1 protège le critère 3.
Quel journal minimal écrire ?
Deux canaux suffisent, et l’un ne remplace pas l’autre.
Canal 1, un fichier de log nommé. Le minimum utile est une ligne par exécution, horodatée, avec un identifiant court du type d’événement et le résultat observé. La ligne doit être lisible sans outil supplémentaire.
LOG=travail/journal.log
echo "$(date -u +%FT%TZ) [info] routine demarree" >> "$LOG"
echo "$(date -u +%FT%TZ) [info] 3 elements traites" >> "$LOG"
echo "$(date -u +%FT%TZ) [error] binaire absent: /usr/local/bin/inexistant" >> "$LOG"
cat "$LOG"
2026-09-15T12:00:59Z [info] routine demarree
2026-09-15T12:00:59Z [info] 3 elements traites
2026-09-15T12:00:59Z [error] binaire absent: /usr/local/bin/inexistant
Canal 2, journalctl. Si la routine tourne comme unité systemd, sa sortie standard et sa sortie d’erreur partent dans le journal de l’unité. La commande de lecture est la même que pour un service système, mais sans les droits du groupe systemd-journal elle renvoie un avertissement, pas un échec :
journalctl --user -u csdf-reprise.service --since "1 minute ago" --no-pager
Hint: You are not currently seeing messages from the system.
Users in groups 'adm', 'systemd-journal' can see all messages.
Pass -q to turn off this notice.
Cet avertissement est attendu sur un compte sans droit de lecture du journal : ce n’est pas un défaut de la commande, c’est une permission. Une routine qui doit être lue par tout le monde utilise le fichier de log ; une routine interne à l’administrateur peut se contenter de journalctl.
Comment bloquer un second lancement quand le premier tourne ?
Deux mécanismes, qui se complètent.
Mécanisme 1, flock. C’est un verrou advisory, lu par les processus qui acceptent de le consulter. Poser le verrou en début de routine, le libérer à la fin, et un second lancement échoue immédiatement avec le code de sortie 1 si le premier tient encore le verrou :
flock /tmp/mon-verrou sleep 0.3 & # premier lancement, tient 0,3 s
sleep 0.05
flock -n /tmp/mon-verrou echo "ok"; echo "rc du deuxieme=$?"
rc du deuxieme=1 (attendu 1, verrouille par le premier)
Le défaut que l’on paie au premier essai est de confondre flock -n fichier commande (forme correcte) avec exec fd> fichier; flock fd (qui marche aussi mais mélange les descripteurs du shell parent). La forme flock fichier commande lit la page de manuel comme « verrou puis exécution » et se relit sans ambiguïté.
Mécanisme 2, fichier sentinelle. Le premier lancement crée un fichier qui contient son identifiant ; le second refuse de continuer si ce fichier existe, et le premier le supprime à la fin (ou à la sortie, via trap).
SENT=travail/.en-cours
if [ -e "$SENT" ]; then
echo "verdict: sentinel deja present -> lancement refuse"
exit 1
fi
echo "pid=$$ etiquette=$(date -u +%FT%TZ) lanceur=routine-test" > "$SENT"
# ... travail ...
rm -f "$SENT"
verdict: sentinel deja present -> lancement refuse
Le sentinelle est plus visible dans ls -l que flock, ce qui aide quand on dépanne à la main. Il est aussi plus fragile : si la routine est tuée par SIGKILL, le fichier reste et bloque le prochain lancement. trap "rm -f $SENT" EXIT couvre les sorties normales ; flock couvre mieux les arrêts brutaux parce que le noyau libère le verrou à la mort du processus.
Le code de retour explicite sur l’échec mérite d’être distingué du code de retour par défaut de bash :
sh -c 'exit 7'; echo "rc=$?"
rc=7
Une routine qui finit en rc=0 après un échec détecté est plus difficile à brancher sur un planificateur qu’une routine qui sort avec son propre code d’erreur.
Comment alerter quand quelque chose ne va pas ?
Trois cibles, par ordre de disponibilité sur une machine Linux nue.
Cible 1, syslog local. L’utilitaire logger écrit une ligne dans le journal système, lisible par journalctl ou par /var/log/syslog :
which logger && logger -p user.err "cybersdf routine en echec" && echo "logger: ok"
logger: ok (ligne envoyee dans /var/log/syslog)
Cette ligne ne prévient personne en direct, mais elle laisse une trace que n’importe quel outil de supervision peut relire.
Cible 2, mail local. Si un MTA (Postfix, Exim, sendmail) est installé et configuré, mail -s "sujet" root < message part dans la boîte locale. Sur la machine de mesure, mail est absent : la commande est inutilisable tant qu'elle n'est pas installée.
which mail >/dev/null && echo "mail: present (utilisable si MTA configure)" || echo "mail: absent"
mail: absent
Cible 3, webhook sur un service que vous contrôlez déjà. Un canal Mattermost, un canal Slack dont vous avez l'URL d'entrée, un point d'entrée HTTP de votre propre supervision : ces cibles acceptent un simple curl ou wget qui n'a pas besoin de clé d'API pour un point d'entrée dédié.
which curl wget
/usr/bin/curl
/usr/bin/wget
La règle de prudence applicable aux trois cibles est la même : testez l'alerte en simulant l'échec, pas en simulant la réussite. Une alerte que l'on n'a jamais vue partir n'est pas une alerte.
Comment redémarrer automatiquement après une coupure ?
Quand la machine redémarre, deux cas se présentent. La routine a un déclencheur temporel (elle doit tourner à 06:30 chaque lundi) ou un déclencheur événementiel (elle doit tourner quand un fichier arrive).
Cas 1, déclencheur temporel avec systemd timer. Le couple *.timer + *.service est l'outil natif. Persistent=true enregistre l'heure du dernier déclenchement ; si la machine était éteinte au moment prévu, le service est déclenché au redémarrage (systemd.timer).
~/.config/systemd/user/csdf-reprise-test.timer :
[Unit]
Description=Test timer reprise (demonstration)
[Timer]
OnCalendar=Mon 06:30
Persistent=true
RandomizedDelaySec=300
[Install]
WantedBy=timers.target
~/.config/systemd/user/csdf-reprise-test.service :
[Unit]
Description=Test de reprise systemd (demonstration)
[Service]
Type=oneshot
ExecStart=/usr/bin/true
[Install]
WantedBy=multi-user.target
Vérifiez la syntaxe des deux fichiers avant de les activer. La commande se lance depuis le dossier qui les contient :
systemd-analyze verify ./csdf-reprise-test.service ./csdf-reprise-test.timer; echo "rc=$?"
rc=0
Une citation mal fermée dans ExecStart= produit une erreur de syntaxe. Vérifiez aussi votre expression calendaire avant de l'utiliser :
systemd-analyze calendar "Mon 06:30"
Original form: Mon 06:30
Normalized form: Mon *-*-* 06:30:00
Next elapse: Mon 2026-09-21 06:30:00 UTC
From now: 5 days left
Cas 2, déclencheur événementiel. systemd-path ou une unité path peut surveiller l'arrivée d'un fichier. Pour des cas plus complexes (plusieurs fichiers, condition sur le contenu), inotifywait du paquet inotify-tools reste une solution lisible : la commande reste bloquante jusqu'à l'événement, et le script qui la suit applique le critère d'arrêt et le journal décrits plus haut.
Limite connue des deux cas. Une routine qui s'exécute au redémarrage ne s'exécute pas si la machine ne redémarre pas mais que l'horloge a sauté. Si votre dépendance au temps est forte, combinez le timer avec une vérification post-exécution qui compare l'horodatage du résultat à l'horloge courante.
Comment tester qu'un échec produit bien une alerte ?
Deux cas négatifs mesurés sur la machine, à exécuter vous-même pour valider votre chaîne.
Cas négatif 1, binaire de la routine introuvable. Une routine qui appelle un script qui n'existe pas sort en code 127 (command not found), que le sous-shell du bash reporte. Le piège est de masquer cet échec par une redirection 2>/dev/null qui transforme l'erreur en silence. Mesure brute :
bash -c '/usr/local/bin/cybersdf-inexistant-xyz' 2>/dev/null; echo "rc=$?"
rc=127
La ligne ajoutée au journal :
2026-09-15T12:00:59Z [error] exec /usr/local/bin/cybersdf-inexistant-xyz -> rc=127
Cas négatif 2, source distante inaccessible. Une URL qui répond 404 n'est pas une erreur de réseau, c'est une réponse valide du serveur qui dit « je n'ai pas ce que vous demandez ». Le code HTTP retourné par curl est 404 ; le code de sortie de curl peut être 0 si on ne lui a pas demandé de l'échouer :
mkdir -p travail/www
( cd travail/www && python3 -m http.server 18099 --bind 127.0.0.1 >/dev/null 2>&1 ) &
sleep 0.5
curl -s -o /dev/null -w "HTTP %{http_code}\n" http://127.0.0.1:18099/absent.json
HTTP 404
Ligne ajoutée au journal :
2026-09-15T12:01:00Z [error] source absente http://127.0.0.1:18099/absent.json -> 404
Ces deux tests ont un défaut commun : ils ne vérifient pas que l'alerte est effectivement partie. Pour cela, ajoutez une étape qui consulte journalctl, lit la dernière ligne du fichier de log, ou interroge le webhook ciblé après l'échec. Une alerte que l'on n'a pas vérifiée est une alerte qui n'existe pas.
Comment prouver que la routine est idempotente ?
Une routine est idempotente si, exécutée deux fois dans un état stable, elle produit le même effet que si elle avait été exécutée une fois. Le test le plus simple est de la relancer et de compter la différence.
mkdir -p travail/scan
python3 -c "import datetime,pathlib,re,sys; d=pathlib.Path('.'); s=datetime.datetime.now().strftime('%Y%m%d-%H%M'); deja=re.compile(r'^\d{8}-\d{4}-'); a=[p for p in sorted(d.glob('*.txt')) if not deja.match(p.name)]; print('a renommer:', len(a))"
a renommer: 0
Zéro parce que le dossier est vide. Sur un dossier peuplé, la même commande sert de garde : un filtre « déjà horodaté » empêche le double préfixe, et le compte a renommer passe à 0 après une première exécution. Relancer la routine ne crée pas de doublon, et c'est ce qui la rend compatible avec un planificateur qui pourrait la déclencher deux fois de suite.
Quels sont les angles que cette méthode ne couvre pas ?
Trois angles, volontairement laissés de côté.
Les alertes qui appellent un téléphone. Cette page n'a pas la prétention d'envoyer un SMS. Si votre besoin réel est d'être réveillé à 3 h du matin, le canal à choisir est une passerelle SMS ou un service de notification push, et le choix du service est un arbitrage que vous ferez en dehors de cette page.
La reprise après une erreur partielle. Une routine qui a écrit 5 fichiers sur 10 et qui échoue au 6ᵉ est dans un état intermédiaire. La méthode décrite ici détecte l'échec, mais la reprise proprement dite (relancer en sautant ce qui a déjà été fait) dépend de votre logique métier : idempotence par hash, par identifiant externe, ou par déplacement dans un dossier « traités ». Ce n'est pas un défaut de la méthode, c'est un travail d'écriture supplémentaire.
La supervision multi-machine. Cette page décrit une machine. La généralisation à plusieurs machines ajoute la question de l'horloge synchronisée, du verrou partagé entre machines (qui n'est plus un fichier local), et de l'observabilité centralisée. Ces sujets sont traités dans la rubrique *Linux & terminal* de ce site.
Repères pour aller plus loin
- Automatiser une petite routine pour les critères d'arrêt avant la première ligne de code.
- Construire un premier workflow n8n pour la version graphique de ces mêmes gardes sur un outil différent.
- Services et journaux systemd pour lire ce qu'un timer a réellement exécuté.
- les gestes sur fichiers et répertoires pour les gestes de déplacement, copie et verrou posés par les scripts.
Une automatisation qui ne s'arrête pas, qui ne parle pas, et qui ne se relance pas est une automatisation qui finira par faire quelque chose de faux en silence. Les cinq angles de cette page sont le minimum pour que cela n'arrive pas.