Un serveur web produit chaque jour des milliers de lignes de journal. Quelque part là-dedans se cachent les réponses aux questions qui comptent : quelles pages sont les plus visitées, qui martèle votre site, combien d’erreurs 404 frustrent vos visiteurs. Ouvrir un fichier de 50 000 lignes dans un éditeur pour chercher à la main, c’est peine perdue. Le trio grep, sed et awk est fait pour ça : trois outils qui filtrent, transforment et résument du texte à la vitesse de l’éclair. À la fin de ce tutoriel, un script lira un journal d’accès et en sortira un rapport de trafic propre.
Dans le fil rouge, Boubacar veut donner à ses clients un petit rapport mensuel sur la fréquentation de leur site, sans payer d’outil d’analytics. Il écrit rapport-trafic.sh, qui digère le journal d’accès du serveur et affiche les pages vedettes, les visiteurs les plus actifs et la répartition des codes de réponse.
🎯 Ce que vous allez apprendre
- Filtrer des lignes avec
grep(recherche, comptage, extraction avec-o, négation avec-v) ; - Transformer du texte avec
sed(substitution, suppression, sélection de lignes) ; - Extraire et agréger des colonnes avec
awk(champs, tableaux associatifs, blocEND) ; - Chaîner ces outils avec
sort,uniq -cetheadpour produire des classements.
🛠️ Ce que vous allez construire
Un script rapport-trafic.sh qui prend un journal d’accès web en argument et affiche : les 5 pages les plus consultées, les 5 adresses IP les plus actives, la répartition des codes HTTP, et le nombre total de requêtes. Un mini-analytics maison, en une commande.
Prérequis
- Avoir suivi les tutoriels précédents (fonctions, pipes, mode strict).
- Test express : si vous savez écrire
cmd1 | cmd2et capturer une sortie avec$(...), vous êtes prêt. - Une notion de base des expressions régulières aide. Pour approfondir, voir le guide des expressions régulières. ⏱️ ~40 minutes.
Étape 1 — Préparer un journal de test
Pour suivre ce tutoriel sans serveur sous la main, fabriquons un petit journal au format « combiné », celui qu’utilisent Apache et Nginx. Chaque ligne contient, dans l’ordre : l’adresse IP, la date, la requête (méthode + chemin), le code HTTP et la taille. On crée quelques lignes représentatives.
cat > acces-test.log <<'EOF'
41.82.10.5 - - [12/May/2026:08:01:10 +0000] "GET /produits HTTP/1.1" 200 1024
197.149.3.8 - - [12/May/2026:08:02:31 +0000] "GET /produits HTTP/1.1" 200 1024
41.82.10.5 - - [12/May/2026:08:03:00 +0000] "GET /panier HTTP/1.1" 200 512
102.64.7.2 - - [12/May/2026:08:04:45 +0000] "GET /vieille-page HTTP/1.1" 404 256
197.149.3.8 - - [12/May/2026:08:05:12 +0000] "GET /produits HTTP/1.1" 200 1024
41.82.10.5 - - [12/May/2026:08:06:20 +0000] "POST /commande HTTP/1.1" 500 128
EOF
Le cat > fichier <<'EOF' est un here-document : tout ce qui suit jusqu’au EOF final est écrit dans le fichier. Les guillemets autour de 'EOF' empêchent Bash d’interpréter le contenu (utile ici, où il y a des caractères spéciaux). Vous avez maintenant un journal de six requêtes : trois IP, quatre pages, et des codes 200, 404, 500. Parfait pour s’exercer.
Étape 2 — grep : filtrer et compter
grep cherche un motif et affiche les lignes qui le contiennent. C’est l’outil de filtrage par excellence. Ses options les plus utiles : -c compte au lieu d’afficher, -v inverse (lignes qui ne contiennent pas le motif), -i ignore la casse, -o n’affiche que la partie qui correspond.
grep "404" acces-test.log # les lignes contenant 404
grep -c "GET /produits" acces-test.log # combien de fois /produits a été demandée
grep -v "200" acces-test.log # les requêtes qui n'ont PAS réussi (hors 200)
La deuxième commande renvoie 3 : la page /produits a été demandée trois fois. La troisième isole les requêtes problématiques (404 et 500). grep comprend les expressions régulières — avec -E pour la syntaxe étendue. Par exemple, grep -E " (404|500) " acces-test.log attrape toutes les erreurs en une fois. Pour maîtriser ces motifs, le tutoriel regex en ligne de commande va beaucoup plus loin.
Étape 3 — sed : transformer le texte
sed est l’éditeur de flux : il applique des transformations à chaque ligne qui passe. Son usage roi est la substitution, s/ancien/nouveau/g (le g final remplace toutes les occurrences de la ligne, pas seulement la première). Il sait aussi supprimer des lignes et n’en sélectionner que certaines.
sed 's/41.82.10.5/[ANONYME]/g' acces-test.log # masque une IP
sed -n '1,3p' acces-test.log # n'affiche que les lignes 1 à 3
sed '/404/d' acces-test.log # supprime les lignes contenant 404
La première commande anonymise une adresse IP dans la sortie (sans toucher au fichier). -n '1,3p' combine deux idées : -n coupe l’affichage automatique, et 1,3p demande d’imprimer (p) les lignes 1 à 3. /404/d supprime (d) les lignes correspondant au motif. Avertissement : l’option sed -i modifie le fichier sur place, sans confirmation ni copie de secours. Ne l’utilisez jamais sans avoir testé la commande sans -i d’abord, et idéalement sur une copie.
✅ Point d’étape — Vous savez filtrer avec
grep(compter, inverser, extraire) et transformer avecsed(substituer, supprimer, sélectionner). Testez chaque commande suracces-test.loget observez la sortie.
Étape 4 — awk : penser en colonnes
awk est le plus puissant des trois. Il voit chaque ligne comme une suite de champs séparés par des espaces, accessibles via $1, $2, etc. $0 est la ligne entière, NF le nombre de champs, NR le numéro de ligne. Dans notre format de log, $1 est l’IP, $7 le chemin demandé, $9 le code HTTP.
awk '{print $1}' acces-test.log # toutes les adresses IP
awk '{print $9, $7}' acces-test.log # code HTTP puis chemin
awk '$9 == 404 {print $7}' acces-test.log # les chemins qui ont renvoyé 404
La troisième commande illustre la force d’awk : $9 == 404 est un filtre (n’agir que sur les lignes dont le 9ᵉ champ vaut 404), suivi d’une action entre accolades (afficher le 7ᵉ champ). Elle répond instantanément à « quelles pages sont cassées ? ». Là où grep filtre des lignes entières, awk raisonne colonne par colonne — exactement ce qu’il faut pour des données tabulées comme un journal.
Étape 5 — Agréger avec les tableaux associatifs d’awk
Le vrai super-pouvoir d’awk, c’est de compter et regrouper. Il dispose de tableaux associatifs (des dictionnaires clé→valeur) et d’un bloc END exécuté après la dernière ligne. Le motif « compter les occurrences » s’écrit en une ligne et remplace tout un pipe.
awk '{compte[$9]++} END {for (code in compte) print compte[code], code}' acces-test.log
À chaque ligne, compte[$9]++ incrémente le compteur associé au code HTTP de cette ligne. Le tableau compte se remplit tout seul : pas besoin de le déclarer. À la fin, le bloc END parcourt le tableau et affiche, pour chaque code, son nombre d’occurrences. Sur notre journal de test, vous obtenez quelque chose comme 4 200, 1 404, 1 500. C’est la répartition des codes de réponse, calculée en un seul passage.
L’approche par pipe donne le même résultat avec des outils plus simples, utile à connaître pour les classements :
awk '{print $7}' acces-test.log | sort | uniq -c | sort -rn | head -5
On lit ça de gauche à droite : awk extrait les chemins, sort les trie (obligatoire avant uniq), uniq -c compte les doublons consécutifs, sort -rn trie par nombre décroissant (-r inverse, -n numérique), head -5 garde le top 5. Ce pipe « sort | uniq -c | sort -rn » est un classique absolu du shell : gravez-le dans votre mémoire, vous l’emploierez partout.
Deux compagnons plus légers méritent une place dans votre trousse, car ils font en un mot ce qui demanderait un awk complet. cut extrait une colonne par position ou par délimiteur ; tr remplace ou supprime des caractères. Quand la tâche est simple, ils sont plus rapides à écrire et à lire.
cut -d' ' -f1 acces-test.log # 1er champ (délimiteur espace) : les IP
cut -d'"' -f2 acces-test.log # ce qui est entre guillemets : la requête
echo "Bonjour Dakar" | tr 'a-z' 'A-Z' # passe en majuscules : BONJOUR DAKAR
tr -d ' ' < fichier.txt # supprime toutes les espaces
cut -d' ' -f1 coupe sur l’espace et garde le premier champ — équivalent ici à awk '{print $1}', en plus court. cut -d'"' -f2 est malin : il découpe sur les guillemets et récupère la requête complète (entre les deux "). tr, lui, travaille caractère par caractère : changer la casse, supprimer des espaces, convertir des séparateurs. Règle pratique : cut et tr pour les cas simples, awk dès qu’il faut filtrer sur une condition ou calculer.
✅ Point d’étape — Vous savez extraire des colonnes avec
awk(etcutpour les cas simples), filtrer sur la valeur d’un champ, et produire un classement de deux façons (tableau associatif ou pipesort|uniq -c|sort -rn). Il reste à packager.
Étape 6 — Assembler rapport-trafic.sh
On réunit tout dans un script structuré, avec les bonnes pratiques du tutoriel 4 : mode strict, fonctions, argument obligatoire. Chaque section du rapport est une fonction lisible. Le journal est lu plusieurs fois — acceptable pour un rapport ponctuel sur un fichier de taille raisonnable.
#!/usr/bin/env bash
#
# rapport-trafic.sh — résume un journal d'accès web.
# Usage : ./rapport-trafic.sh /var/log/nginx/access.log
# Boîte à outils Atelier — tutoriel 5.
set -euo pipefail
journal="${1:?Usage : $0 <fichier-log>}"
[[ -s "$journal" ]] || { echo "❌ Journal vide ou absent : $journal" >&2; exit 1; }
titre() { printf '\n=== %s ===\n' "$1"; }
total=$(wc -l < "$journal")
echo "Rapport de trafic — $journal"
echo "Requêtes totales : $total"
titre "Top 5 des pages"
awk '{print $7}' "$journal" | sort | uniq -c | sort -rn | head -5 || true
titre "Top 5 des visiteurs (IP)"
awk '{print $1}' "$journal" | sort | uniq -c | sort -rn | head -5 || true
titre "Répartition des codes HTTP"
awk '{compte[$9]++} END {for (c in compte) printf "%6d %s\n", compte[c], c}' "$journal" | sort -rn
titre "Pages en erreur (404 / 500)"
awk '$9 ~ /^(404|500)$/ {print $9, $7}' "$journal" | sort | uniq -c | sort -rn
Quelques points à noter. wc -l < "$journal" compte les lignes (le < évite que wc affiche aussi le nom du fichier). La fonction titre standardise les en-têtes de section. Le || true après les head -5 est volontaire : quand head s’arrête à la 5ᵉ ligne il ferme le tuyau, et le sort en amont reçoit un signal SIGPIPE ; avec pipefail, ce faux échec ferait planter le script sur un gros journal — || true le neutralise. Pour les erreurs, $9 ~ /^(404|500)$/ utilise une expression régulière dans awk (l’opérateur ~ signifie « correspond à »), avec les ancres ^ et $ pour un code exact. Lancez ./rapport-trafic.sh acces-test.log : vous obtenez un rapport complet et lisible.
✅ Point d’étape final — Le script affiche quatre sections classées. Sur un vrai journal Nginx (
/var/log/nginx/access.log), il révèle immédiatement vos pages vedettes et vos erreurs. Vous tenez votre mini-analytics.
🐞 Pièges fréquents
| Symptôme / erreur | Cause probable | Correctif |
|---|---|---|
uniq ne regroupe pas les doublons |
uniq ne voit que les lignes consécutives |
Toujours sort avant uniq |
Mauvaise colonne extraite par awk |
Format de log différent (séparateur, champs décalés) | Vérifier avec awk '{print NF}' et ajuster -F |
sed -i a abîmé le fichier |
Modification sur place sans test préalable | Tester sans -i d’abord ; garder une copie |
Le script s’arrête sur un grep sans résultat |
Mode strict + grep qui ne trouve rien (code 1) |
Ajouter || true au grep concerné |
| Tri alphabétique au lieu de numérique | sort sans -n |
Utiliser sort -rn pour un classement par nombre |
🌍 Adaptation au contexte ouest-africain
Les outils d’analytics hébergés coûtent cher en abonnement et envoient les données de vos clients à l’étranger. Pour une boutique ou une association locale, un rapport awk maison suffit largement à savoir ce qui marche, et garde les données sur votre propre serveur — un vrai argument de souveraineté et de coût. grep, sed et awk sont présents sur le moindre VPS, ne consomment quasiment rien, et traitent des journaux de plusieurs centaines de milliers de lignes en une fraction de seconde, même sur une petite machine. Idéal là où le matériel est modeste et la bande passante précieuse.
✅ Récapitulatif
Vous savez désormais faire parler du texte. grep filtre et compte les lignes (-c, -v, -o, -E) ; sed transforme le flux (substitution s///g, suppression, sélection de lignes) ; awk raisonne en colonnes, filtre sur la valeur d’un champ et agrège avec ses tableaux associatifs et son bloc END. Vous maîtrisez le pipe-classement sort | uniq -c | sort -rn. rapport-trafic.sh transforme un journal brut en rapport exploitable — une compétence qui sert pour les logs, les CSV, et toute donnée tabulée.
🧾 Aide-mémoire
| Commande | Rôle |
|---|---|
grep -c "motif" f |
Compte les lignes contenant le motif |
grep -v "motif" f |
Lignes qui ne contiennent PAS le motif |
grep -oE "motif" f |
Extrait seulement la partie qui correspond |
sed 's/a/b/g' f |
Remplace a par b sur chaque ligne |
sed -n '1,5p' f |
Affiche les lignes 1 à 5 |
awk '{print $1}' f |
Affiche le 1er champ de chaque ligne |
awk '$9==404' f |
Lignes dont le 9e champ vaut 404 |
awk '{c[$1]++} END{...}' |
Compte les occurrences (tableau associatif) |
... | sort | uniq -c | sort -rn |
Classement par fréquence |
💪 À vous de jouer
Ajoutez à rapport-trafic.sh une section qui calcule le pourcentage de requêtes en erreur (codes ≥ 400) par rapport au total. Indice : awk peut compter dans un seul passage et faire la division dans END.
Voir une solution
titre "Taux d'erreur"
awk '{ total++; if ($9 >= 400) erreurs++ }
END { printf "%.1f%% (%d/%d)\n", (erreurs/total)*100, erreurs, total }' "$journal"
On compte total et erreurs au fil des lignes, puis END affiche le pourcentage avec printf "%.1f%%" (un chiffre après la virgule, et %% pour afficher un vrai signe pourcent).
Tutoriels frères
- Fonctions, arguments et scripts robustes — la structure qui encadre ce script.
- Automatiser ses scripts avec cron — faire tourner ce rapport tout seul chaque semaine.
Pour aller plus loin
- 🔝 Retour au guide principal : Bash scripting : le guide complet.
- Pour les motifs de recherche : le guide complet des expressions régulières et le tutoriel grep/sed/ripgrep.
- GNU Awk User’s Guide (source officielle).
FAQ
Q : Quand utiliser grep, sed ou awk ?
R : grep pour trouver des lignes, sed pour modifier du texte ligne à ligne, awk pour extraire et calculer sur des colonnes. Beaucoup de tâches se font avec les trois ; choisissez le plus simple qui résout votre cas.
Q : Mon log a un séparateur différent (virgule, point-virgule). Comment faire ?
R : Précisez-le à awk avec -F : awk -F';' '{print $2}' pour un fichier à points-virgules, -F',' pour un CSV simple. Attention : un vrai CSV avec des virgules dans les champs demande un outil dédié.
Q : awk ou gawk ?
R : awk est souvent un lien vers une implémentation (mawk, ou gawk de GNU). Les commandes de ce tutoriel marchent partout. gawk ajoute des extensions (fonctions de temps, tri) que vous découvrirez au besoin.
Q : Comment traiter des journaux très volumineux sans saturer la mémoire ?
R : grep, sed et awk lisent en flux, ligne par ligne, donc ils gèrent des fichiers énormes. Le point sensible est sort, qui peut charger beaucoup en mémoire ; sur de très gros volumes, sort bascule sur disque automatiquement, ou vous filtrez d’abord avec awk.
📚 Ressources et références
- GNU Awk User’s Guide : la référence complète sur awk.
- GNU sed Manual et GNU grep Manual (sources officielles).
- Expressions régulières en ligne de commande — pour des motifs de recherche avancés.
Mots-clés : grep sed awk, traitement de texte bash, analyser un log, awk colonnes, tableau associatif awk, sort uniq, rapport-trafic, parser access log.