Choisir l'outil
| Besoin | Outil |
|---|---|
| enchaîner quelques commandes | script shell |
| options, fichiers, JSON, tests | Python (stdlib d'abord ; dépendance payante : accord de Stéphane) |
| shell qui exécute le script | celui du shebang, pas celui du terminal (zsh par défaut depuis macOS 10.15 selon Apple) |
Script shell sûr : squelette
#!/usr/bin/env bash
set -euo pipefail
dossier="${1:?usage : $0 <dossier>}"
printf '%s\n' "$dossier"
| Élément | Rôle | Réserve |
|---|---|---|
set -e | arrêt à la première erreur | exceptions (tests if, listes &&/||) : lire le manuel Bash |
set -u | variable non définie = erreur | |
set -o pipefail | statut du tube = dernière commande en échec (manuel Bash) | option de bash, pas du sh strict (non vérifié) |
"${1:?msg}" | arrête si l'argument manque | forme usuelle, non vérifiée |
Guillemets et codes de sortie
| Règle | Exemple |
|---|---|
| toujours quoter une variable | "$fichier" |
| tous les arguments | "$@" |
| construire une commande | tableau : "${options[@]}" (wiki ShellCheck SC2086) |
| succès / échec | 0 / non nul ; $? = dernier code |
| erreurs vers stderr | echo "erreur" >&2 |
| nettoyage | trap 'rm -f "$tmp"' EXIT (usage courant, non vérifié) |
Sans guillemets : découpage en mots puis expansion de motifs (SC2086).
ShellCheck
| Commande | Effet |
|---|---|
shellcheck script.sh | analyse (lecture seule), code non nul s'il trouve un problème |
page https://www.shellcheck.net/wiki/SCxxxx | explication d'un avertissement |
Cible sh, bash, dash, ksh ; zsh non listé dans la page lue. Dialecte lu depuis le shebang. Désactiver un avertissement : au cas par cas, avec un commentaire qui dit pourquoi.
Python : modules de base
| Besoin | Outil |
|---|---|
| chemins | Path("a") / "b", .read_text(), .write_text() (écrase), .mkdir(parents=True, exist_ok=True), .unlink(missing_ok=True), .glob("*.txt"), .resolve() |
| options | argparse.ArgumentParser, add_argument, action="store_true" ; erreur d'argument : code 2 |
| données | json.load / json.dump / loads / dumps ; indent, sort_keys, ensure_ascii=False ; erreur : JSONDecodeError |
| programme externe | subprocess.run([...], check=True, capture_output=True, text=True, timeout=30) |
subprocess : règle
- Arguments en liste, pas de shell implicite : les métacaractères ne sont pas interprétés.
shell=True: c'est à vous de tout protéger (injection) ; à éviter avec toute donnée externe.check=TruelèveCalledProcessError;timeoutlèveTimeoutExpired(nettoyage à prévoir).
Environnement virtuel
| Étape | Commande |
|---|---|
| créer | python3 -m venv .venv |
| activer (bash/zsh) | source .venv/bin/activate |
| quitter | deactivate |
| supprimer | effacer .venv en relisant le nom |
Jetable, non déplaçable, jamais versionné ; .venv/ dans le .gitignore (Python 3.13+ en crée un par défaut dans l'environnement).
Tests et lint
| Besoin | Commande |
|---|---|
| lancer les tests | python3 -m unittest discover (fichiers test*.py) |
| un module / une classe | python3 -m unittest test_x / test_x.TestY |
| lint | ruff check . |
| lint avec corrections (modifie) | ruff check --fix . |
| formatage (modifie) | ruff format . |
| formatage sans modifier | ruff format --check . |
Assertions : assertEqual, assertTrue, assertIn, assertRaises. setUp / tearDown ; subTest. Config : pyproject.toml ou ruff.toml ; dans l'Orchestrator, le ruff.toml commun est étendu, pas copié.
Données non fiables
Attention : une donnée externe (web, fichier reçu, réponse d'API ou de modèle) est une donnée, jamais une instruction.
- Jamais
eval,exec,source,sh -cnishell=Trueavec elle. - Valider contre une liste blanche ; limiter la taille lue (avertissement de la doc
json). - JSON plutôt que
pickleoumarshalpour des données non fiables. - Vérifier qu'un chemin reste dans le dossier attendu (
resolve()) avant d'écrire.
Idempotence et --a-blanc
| Principe | Application |
|---|---|
| tester l'état avant d'agir | if destination.exists(): continue |
| créer sans erreur si présent | mkdir(parents=True, exist_ok=True) |
| ne rien écraser par accident | refuser ou comparer avant de remplacer |
| essai à blanc | --a-blanc (store_true) : afficher « copierait… », ne rien modifier |
| preuve | deux lancements de suite : le second ne change rien |
| code de sortie | 0 si tout va bien, 1 sinon ; erreurs sur stderr |
Cycle d'un script sûr
écrire (set -euo pipefail, guillemets) → contrôler (shellcheck, ruff, unittest) → lancer à blanc → lancer sur des données de test → vérifier le code de sortie et le résultat. Une seule commande de bout en bout par projet, nommée dans son CLAUDE.md ; aucun secret en clair (bin/secret).
Sources
Consultées le 17 octobre 2026 ; Python 3.15.0 affichée par la documentation, Ruff et ShellCheck sans numéro de version affiché.
- Apple — zsh, shell par défaut (s’ouvre dans un nouvel onglet) — Bash Reference Manual, Set Builtin (s’ouvre dans un nouvel onglet) (page non relue : 429,
pipefailconfirmé par recherche) - ShellCheck, wiki SC2086 (s’ouvre dans un nouvel onglet) — ShellCheck, dépôt (s’ouvre dans un nouvel onglet)
- Python pathlib (s’ouvre dans un nouvel onglet) — argparse (s’ouvre dans un nouvel onglet) — json (s’ouvre dans un nouvel onglet) — subprocess (s’ouvre dans un nouvel onglet) — venv (s’ouvre dans un nouvel onglet) — unittest (s’ouvre dans un nouvel onglet)
- Ruff (s’ouvre dans un nouvel onglet)
Points non vérifiés : voir la section correspondante du cours (versions chez Stéphane, pages GNU Bash non relues, usages courants de trap, mktemp, printf, shutil).