Exercice 11 : Traitement de texte avancé avec awk et sed
🎯 Objectifs
À la fin de cet exercice, vous serez capable de :
- ✅ Utiliser
awkpour extraire, filtrer et calculer sur des colonnes - ✅ Écrire des programmes awk avec
BEGIN,END, conditions et variables - ✅ Utiliser
sedpour des substitutions, insertions et suppressions - ✅ Enchaîner
grep | awk | sed | sort | uniqdans des pipelines efficaces - ✅ Analyser des fichiers de log réels avec ces outils
Durée estimée : 40 minutes
Difficulté : ⭐⭐⭐☆☆ (Avancé)
Prérequis : Exercice 05 (pipes et texte) complété, notion de regex
📖 Contexte
awk et sed sont les couteaux suisses du traitement de texte Linux. En DevOps, ils servent à analyser des logs, transformer des configurations, générer des rapports et traiter des fichiers CSV exportés depuis des outils de monitoring. Maîtriser ces deux outils permet de faire en une ligne ce qui prendrait 50 lignes de Python.
📋 Énoncé
Analysez des logs d'accès web et des fichiers de configuration avec awk et sed.
🧭 Déroulement de l'exercice
Tâche 1 : Fondamentaux awk
# Créer un CSV de test
cat > serveurs.csv << 'EOF'
nom,ip,cpu,memoire,statut
web-01,10.0.1.1,45,72,ok
web-02,10.0.1.2,88,65,ok
db-01,10.0.2.1,23,91,ok
db-02,10.0.2.2,95,89,alerte
cache-01,10.0.3.1,12,45,ok
backup-01,10.0.4.1,5,30,maintenance
EOF
# Afficher les colonnes 1 et 5 (nom et statut)
awk -F',' '{print $1, $5}' serveurs.csv
# Ignorer l'en-tête (NR = numéro de ligne)
awk -F',' 'NR > 1 {print $1, $5}' serveurs.csv
# Filtrer les serveurs en alerte ou maintenance
awk -F',' 'NR > 1 && ($5 == "alerte" || $5 == "maintenance")' serveurs.csv
# Calculer la moyenne CPU
awk -F',' 'NR > 1 {total += $3; nb++} END {printf "CPU moyen : %.1f%%\n", total/nb}' serveurs.csv
# Compter par statut
awk -F',' 'NR > 1 {count[$5]++} END {for (s in count) print s": "count[s]}' serveurs.csvVérification : La moyenne CPU doit être calculée et les statuts comptés.
Tâche 2 : Analyse de logs Apache/Nginx
# Générer un faux fichier de log (format Combined Log Format)
cat > access.log << 'EOF'
192.168.1.1 - - [29/Apr/2025:10:00:01 +0000] "GET /api/users HTTP/1.1" 200 1234 "-" "Mozilla/5.0"
192.168.1.2 - - [29/Apr/2025:10:00:02 +0000] "POST /api/login HTTP/1.1" 401 89 "-" "curl/7.68"
192.168.1.1 - - [29/Apr/2025:10:00:03 +0000] "GET /api/products HTTP/1.1" 200 5678 "-" "Mozilla/5.0"
10.0.0.5 - - [29/Apr/2025:10:00:04 +0000] "GET /admin HTTP/1.1" 403 45 "-" "Python-urllib/3.9"
192.168.1.3 - - [29/Apr/2025:10:00:05 +0000] "GET /api/users HTTP/1.1" 200 1234 "-" "Mozilla/5.0"
10.0.0.5 - - [29/Apr/2025:10:00:06 +0000] "GET /etc/passwd HTTP/1.1" 404 23 "-" "Python-urllib/3.9"
192.168.1.2 - - [29/Apr/2025:10:00:07 +0000] "POST /api/login HTTP/1.1" 200 567 "-" "curl/7.68"
192.168.1.4 - - [29/Apr/2025:10:00:08 +0000] "GET /api/products HTTP/1.1" 500 12 "-" "Mozilla/5.0"
10.0.0.5 - - [29/Apr/2025:10:00:09 +0000] "GET /.env HTTP/1.1" 404 23 "-" "Python-urllib/3.9"
192.168.1.1 - - [29/Apr/2025:10:00:10 +0000] "DELETE /api/users/42 HTTP/1.1" 204 0 "-" "Mozilla/5.0"
EOF
echo "=== Top 3 des IPs les plus actives ==="
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -3
echo ""
echo "=== Codes HTTP par fréquence ==="
awk '{print $9}' access.log | sort | uniq -c | sort -rn
echo ""
echo "=== Requêtes suspectes (user-agent Python) ==="
awk '$12 ~ /Python/' access.log | awk '{print $1, $7, $9}'
echo ""
echo "=== Taille totale transférée par IP ==="
awk '{bytes[$1] += $10} END {
for (ip in bytes)
printf "%s : %d octets\n", ip, bytes[ip]
}' access.log | sort -t: -k2 -rn
echo ""
echo "=== Rapport de sécurité ==="
awk 'BEGIN {
print "IP\t\tTentatives\tURLs suspectes"
print "---\t\t----------\t--------------"
}
$9 >= 400 {
tentatives[$1]++
}
$7 ~ /passwd|shadow|\.env|\.git/ {
suspects[$1]++
}
END {
for (ip in tentatives)
printf "%s\t%d\t\t%d\n", ip, tentatives[ip], suspects[ip]+0
}' access.logTâche 3 : sed pour les transformations
# Fichier de configuration à transformer
cat > app.conf << 'EOF'
# Configuration de l'application
database_host=localhost
database_port=5432
database_name=myapp_dev
database_user=developer
log_level=DEBUG
api_url=http://localhost:8080
workers=2
# Fin de configuration
EOF
echo "=== Remplacer 'dev' par 'prod' ==="
sed 's/dev/prod/g' app.conf
echo ""
echo "=== Remplacer seulement dans les lignes de config (pas les commentaires) ==="
sed '/^#/!s/localhost/db.prod.internal/g' app.conf
echo ""
echo "=== Supprimer les lignes de commentaires ==="
sed '/^#/d' app.conf
echo ""
echo "=== Supprimer les lignes vides ==="
sed '/^$/d' app.conf
echo ""
echo "=== Insérer une ligne avant une ligne spécifique ==="
sed '/^log_level/i # Niveau de log (DEBUG, INFO, WARNING, ERROR)' app.conf
echo ""
echo "=== Transformation complète pour la production ==="
sed \
-e 's/localhost/db.prod.internal/g' \
-e 's/5432/5432/' \
-e 's/myapp_dev/myapp_prod/' \
-e 's/developer/app_user/' \
-e 's/log_level=DEBUG/log_level=WARNING/' \
-e 's|api_url=http://localhost:8080|api_url=https://api.monapp.com|' \
-e 's/workers=2/workers=8/' \
app.confTâche 4 : Script d'analyse de logs complet
cat > analyser-logs.sh << 'EOF'
#!/usr/bin/env bash
set -euo pipefail
FICHIER_LOG="${1:-access.log}"
RAPPORT="${2:-/tmp/rapport-$(date +%Y%m%d).txt}"
[[ -f "$FICHIER_LOG" ]] || { echo "Fichier introuvable : $FICHIER_LOG" >&2; exit 1; }
{
echo "RAPPORT D'ANALYSE - $(date '+%Y-%m-%d %H:%M:%S')"
echo "Fichier : $FICHIER_LOG ($(wc -l < "$FICHIER_LOG") lignes)"
echo "=============================================="
echo ""
echo "--- Résumé des codes HTTP ---"
awk '{print $9}' "$FICHIER_LOG" | sort | uniq -c | sort -rn
echo ""
echo "--- Top 5 des URLs les plus consultées ---"
awk '$9 == 200 {print $7}' "$FICHIER_LOG" | sort | uniq -c | sort -rn | head -5
echo ""
echo "--- Erreurs 5xx ---"
awk '$9 >= 500 && $9 < 600 {print $1, $7, $9}' "$FICHIER_LOG" || echo "Aucune erreur 5xx"
echo ""
echo "--- IPs avec plus de 3 erreurs 4xx ---"
awk '$9 >= 400 && $9 < 500 {err[$1]++} END {
for (ip in err) if (err[ip] >= 3) print ip, err[ip]" erreurs"
}' "$FICHIER_LOG" || echo "Aucune IP suspecte détectée"
} | tee "$RAPPORT"
echo ""
echo "Rapport sauvegardé : $RAPPORT"
EOF
chmod +x analyser-logs.sh
./analyser-logs.sh access.log✅ Vérification du résultat
awkcalcule la moyenne CPU depuisserveurs.csv- Les requêtes Python suspectes sont identifiées dans les logs
sedtransformeapp.confpour la productionanalyser-logs.shgénère un rapport dans/tmp/
💡 À retenir
| Outil | Usage principal |
|---|---|
awk | Colonnes, calculs, agrégations, statistiques |
sed | Substitutions, insertions, suppressions de lignes |
grep | Filtrage, recherche de patterns |
| Pipeline | grep | awk | sed | sort | uniq -c | sort -rn | head |
awk en une phrase : pour chaque ligne ({}), avec des actions spéciales au début (BEGIN {}) et à la fin (END {}), accéder aux colonnes $1, $2... et au numéro de ligne NR.
✨ Solution Complète
# Analyser des logs en une commande
# Top 5 IPs avec erreurs 4xx
awk '$9 >= 400 && $9 < 500 {err[$1]++} END {
for (ip in err) print err[ip], ip
}' access.log | sort -rn | head -5
# Transformer une config de dev à prod
sed -e 's/localhost/prod-server/g' -e 's/DEBUG/INFO/g' app.conf > app.prod.conf