infra / linux-bash
Redirections et pipes avancés
Explication
Chaque commande dispose en réalité de trois canaux de communication distincts : l'entrée standard (ce qu'elle lit), la sortie standard (son résultat normal) et la sortie d'erreur (ses messages d'erreur). Comprendre ces trois flux et comment les combiner via des pipes transforme le terminal en un véritable outil de composition, où de petites commandes simples s'assemblent pour résoudre des problèmes complexes.
Ce que vous allez apprendre
- Distinguer stdin, stdout et stderr et savoir rediriger chacun séparément
- Comprendre la différence entre
>(écrase) et>>(ajoute) avant de perdre un fichier important - Combiner plusieurs commandes avec
|pour construire une pipeline de traitement de logs - Utiliser
teepour logger et afficher un résultat en même temps - Distinguer
$(...)(capture le résultat) de<(...)(traite un résultat comme un fichier)
Dans quel contexte ?
Un ingénieur DevOps doit identifier les 10 adresses IP qui sollicitent le plus un serveur web, à partir du fichier /var/log/nginx/access.log qui contient plusieurs millions de lignes. Plutôt que d'écrire un programme dédié, il assemble en une seule ligne des outils déjà présents sur toute machine Linux : extraire une colonne, trier, compter les doublons, trier à nouveau, garder le haut du classement. C'est la philosophie Unix appliquée à un vrai problème de production.
Partons d'un constat simple : une commande a trois canaux
Séparer stdout et stderr est une décision de conception intelligente. Cela permet, par exemple, d'afficher les erreurs à l'écran tout en enregistrant uniquement le résultat utile dans un fichier, sans jamais les mélanger.
| Opérateur | Effet | Risque |
|---|---|---|
> | Redirige stdout, écrase le fichier | Perte de données si le fichier existait |
>> | Redirige stdout, ajoute à la fin | Aucun, le contenu existant est préservé |
2> | Redirige uniquement stderr | — |
2>&1 | Fusionne stderr dans stdout | Ordre important : doit venir après > |
Piège dangereux
Écraser un fichier de log de production avec commande > app.log au lieu d'ajouter avec >> supprime instantanément tout l'historique précédent, souvent sans possibilité de retour en arrière. Le réflexe simple : toujours vérifier l'opérateur de redirection avant de l'exécuter sur un fichier important, et préférer >> par défaut pour un fichier de log.
Une fois ces trois flux compris, voici l'idée qui change tout : le pipe
L'idée fondatrice d'Unix est qu'un petit outil, qui fait une seule chose très bien, peut être combiné à d'autres via des tuyaux (|). Plutôt qu'un programme monolithique qui ferait tout, on assemble des briques simples : trier, filtrer, compter, transformer.
Bonne pratique
Pour un build ou un déploiement long, utilise tee : long_build.sh 2>&1 | tee build.log affiche le résultat en direct dans le terminal ET l'enregistre dans un fichier, sans avoir à choisir entre les deux.
Pour aller plus loin, une nuance utile : deux types de substitution
$(...) capture le résultat d'une commande comme une simple valeur texte, utilisable ensuite dans une variable — c'est la version la plus simple et la plus fréquente. <(...) va un cran plus loin : elle fait croire à une commande qu'elle lit un vrai fichier, alors qu'en réalité elle lit la sortie d'une autre commande en temps réel, très utile pour comparer deux résultats de commandes sans créer de fichiers temporaires.
Maintenant que tu sais combiner des commandes entre elles, la suite logique est d'apprendre à décrire des motifs de texte complexes avec les expressions régulières, grep, sed et awk.
Commandes & code
Redirections et pipes avancés
# Descripteurs de fichiers standards : 0=stdin, 1=stdout, 2=stderr
commande > sortie.txt # redirige stdout (écrase le fichier)
commande >> sortie.txt # redirige stdout (ajoute à la fin)
commande 2> erreurs.txt # redirige uniquement stderr
commande > tout.txt 2>&1 # stdout ET stderr dans le même fichier (ordre important)
commande &> tout.txt # raccourci équivalent à la ligne précédente
commande 2>/dev/null # jette les erreurs à la poubelle
commande < entree.txt # utilise un fichier comme entrée standard
# Pipe : la sortie d'une commande devient l'entrée de la suivante
ps aux | grep nginx | grep -v grep
# tee : écrit dans un fichier ET affiche à l'écran en même temps
long_build.sh 2>&1 | tee build.log
long_build.sh 2>&1 | tee -a build.log # -a pour ajouter au lieu d'écraser
# Substitution de commande : injecte le résultat d'une commande dans une autre
echo "Aujourd'hui on est le $(date +%Y-%m-%d)"
fichiers=$(find . -name "*.log")
# Substitution de processus : traite une commande comme si c'était un fichier
diff <(sort fichier1.txt) <(sort fichier2.txt)
while read -r ligne; do echo "-> $ligne"; done < <(grep ERROR app.log)
# Here-document : injecte un bloc de texte multi-lignes en entrée standard
cat <<EOF > config.yml
env: production
debug: false
replicas: 3
EOF
# Here-string : injecte une seule ligne
grep "pattern" <<< "$une_variable"
# Combiner plusieurs pipes pour une pipeline de traitement réaliste
cat access.log \
| awk '{print $1}' \
| sort \
| uniq -c \
| sort -rn \
| head -10 # top 10 des IP qui appellent le plus le serveur
# xargs : transforme une liste (stdin) en arguments de commande
find . -name "*.tmp" | xargs rm -f
cat urls.txt | xargs -n1 -P4 curl -O # 4 téléchargements en parallèleRésumé
>écrase,>>ajoute,2>&1fusionne stderr dans stdout (respecter l'ordre).teepermet de logger ET d'afficher en même temps, très utile pour un build/déploiement.$(...)capture une sortie,<(...)la présente comme un fichier temporaire.
Exercices pratiques
Mission : trouver les IP les plus actives sans écraser les logs de prod
Objectif : Construire une pipeline d'analyse de logs, journaliser un déploiement avec tee, et comprendre pourquoi un cron efface ses propres traces.
Contexte
Le fichier /var/log/nginx/access.log contient plusieurs millions de lignes. Tu dois en extraire les 10 IP les plus actives, tout en veillant à ne jamais écraser accidentellement un fichier de log de production.