Aller au contenu
Administration Linux medium

Traiter des données avec awk

30 min de lecture

awk est un langage de traitement de flux orienté enregistrements. Là où grep filtre des lignes entières et cut extrait des colonnes fixes, awk travaille sur la structure des données : il découpe chaque ligne en champs, applique des conditions par champ, accumule des valeurs dans des variables et reformate la sortie. Pas besoin d'un script Python pour calculer le total d'un CSV ou compter les codes d'erreur dans un log, awk fait ça en une ou deux lignes.

C'est un objectif direct LFCS ("Compare and Manipulate File Content") et RHCSA ("Process text streams using filters").

  • Accéder aux champs automatiques $1, $NF, NR et NF
  • Changer le séparateur avec -F pour parser :, , ou n'importe quel délimiteur
  • Filtrer des lignes avec des conditions numériques ($3 >= 1000) et des regex ($1 ~ /pattern/)
  • Utiliser BEGIN et END pour afficher des en-têtes et calculer des totaux
  • Construire un tableau associatif pour compter des occurrences par valeur
  • Reformater proprement une sortie avec printf

awk intervient dès que vous avez besoin de raisonner sur la structure d'une ligne plutôt que sur son contenu texte brut :

  • extraire le login et le shell de tous les comptes système depuis /etc/passwd
  • calculer le chiffre d'affaires total depuis un export CSV
  • compter les codes HTTP 4xx et 5xx par IP dans un access.log
  • afficher uniquement les processus d'un utilisateur depuis la sortie de ps
  • reformater une sortie de commande en tableau lisible avec des colonnes alignées

awk n'est pas un langage de script complet comme Python, mais pour les opérations de traitement de flux en ligne de commande, il est plus rapide à écrire, plus lisible qu'un pipeline complexe, et disponible partout.

Toutes les sorties de ce guide proviennent des deux fichiers ci-dessous. Créez-les dans un répertoire de travail pour reproduire chaque commande et comparer votre résultat au résultat affiché. Le premier est un extrait de journal au format Combined Log produit par Apache et nginx ; le second est un export CSV de ventes avec une ligne d'en-tête, exactement le genre de fichier qu'on récupère d'un tableur.

Fenêtre de terminal
cat > access.log <<'EOF'
192.168.1.10 - alice [09/Apr/2026:08:12:03 +0200] "GET /index.html HTTP/1.1" 200 1043
10.0.0.5 - bob [09/Apr/2026:08:12:44 +0200] "POST /login HTTP/1.1" 401 219
192.168.1.10 - alice [09/Apr/2026:08:13:01 +0200] "GET /dashboard HTTP/1.1" 200 5120
10.0.0.99 - - [09/Apr/2026:08:13:37 +0200] "GET /admin HTTP/1.1" 403 162
10.0.0.5 - bob [09/Apr/2026:08:14:02 +0200] "POST /login HTTP/1.1" 200 874
172.16.0.3 - charlie [09/Apr/2026:08:14:50 +0200] "GET /api/users/42 HTTP/1.1" 404 145
192.168.1.10 - alice [09/Apr/2026:08:15:12 +0200] "DELETE /api/orders/7 HTTP/1.1" 500 302
EOF
cat > ventes.csv <<'EOF'
produit,region,quantite,prix_unitaire
laptop,nord,5,1200
serveur,sud,2,3500
switch,nord,10,450
laptop,est,3,1200
serveur,nord,1,3500
switch,sud,8,450
disque,est,20,80
EOF

Le délimiteur <<'EOF' est entre guillemets simples volontairement : sans eux, le shell interpréterait les $ et les guillemets présents dans le journal avant même que le fichier soit écrit.

awk découpe chaque ligne en champs selon un séparateur (espace par défaut) et les numérote :

VariableContenu
$0La ligne entière
$1Premier champ
$2Deuxième champ
$NFDernier champ (NF = nombre de champs)
NRNuméro de la ligne courante
NFNombre de champs sur la ligne courante

Le format Combined Log place toujours l'information au même rang : l'adresse du client en premier, le code de statut HTTP en neuvième position. C'est ce qui rend awk utilisable ici sans regex : on désigne une donnée par sa position, pas par sa forme. Le programme entre accolades s'exécute une fois par ligne lue, et print sépare ses arguments par une espace.

Fenêtre de terminal
awk '{print NR, $1, $9}' access.log
1 192.168.1.10 200
2 10.0.0.5 401
3 192.168.1.10 200
4 10.0.0.99 403
5 10.0.0.5 200
6 172.16.0.3 404
7 192.168.1.10 500
  • $1 = adresse IP
  • $9 = code de statut HTTP (le 9e champ dans le format Combined Log)
  • NR = numéro de ligne

Par défaut, awk sépare les champs sur les espaces. L'option -F change ce séparateur :

Le fichier /etc/passwd liste les comptes locaux du système, un par ligne, avec sept champs séparés par des deux-points. cut -d: -f1,7 ferait la même extraction, mais awk permet en plus de filtrer sur la valeur d'un champ, par exemple ne garder que les comptes dont le shell est /bin/bash. Attention, -F: doit précéder le programme sur la ligne de commande. Placée après, l'option est prise pour un nom de fichier et awk s'arrête sur fatal: cannot open file '-F:' for reading. Vos valeurs différeront des miennes : ce sont les comptes de la machine de test.

Fenêtre de terminal
awk -F: '{print $1, $7}' /etc/passwd
root /bin/bash
daemon /usr/sbin/nologin
bob /bin/bash
alice /bin/zsh
sshd /usr/sbin/nologin
  • $1 = nom d'utilisateur
  • $7 = shell de connexion (7e champ séparé par :)
Fenêtre de terminal
# Afficher produit et total (quantité × prix unitaire), en sautant l'en-tête
awk -F, 'NR > 1 {print $1, $3*$4}' ventes.csv
laptop 6000
serveur 7000
switch 4500
laptop 3600
serveur 3500
switch 3600
disque 1600

NR > 1 saute la première ligne (en-tête). Les champs $3 et $4 sont multipliés directement, awk gère l'arithmétique nativement.

Un programme awk s'écrit sous la forme condition { action }. Quand la condition est omise, l'action s'applique à toutes les lignes ; quand l'action est omise, awk affiche la ligne entière. Les trois formes de condition qui suivent, numérique, chaîne et regex, couvrent la quasi-totalité des filtrages d'administration.

awk traite un champ qui ressemble à un nombre comme un nombre dans une comparaison. C'est la différence décisive avec grep, qui ne connaît que du texte : grep 4 trouverait aussi bien 404 que 1043, alors que $9 >= 400 compare réellement des valeurs.

Le piège arrive quand le champ ne ressemble pas à un nombre. awk bascule alors en comparaison de chaînes, et le résultat surprend : echo abc | awk '$1 >= 400' affiche la ligne, parce que la chaîne "abc" est lexicalement supérieure à "400". Aucun message d'erreur, juste des lignes parasites dans votre résultat. Sur un fichier dont vous ne maîtrisez pas la propreté, forcez la conversion avec $1+0 >= 400 : le +0 impose une comparaison numérique et écarte les valeurs non numériques.

Fenêtre de terminal
# Requêtes avec code HTTP >= 400 (erreurs)
awk '$9 >= 400 {print $1, $9, $7}' access.log
10.0.0.5 401 /login
10.0.0.99 403 /admin
172.16.0.3 404 /api/users/42
192.168.1.10 500 /api/orders/7

L'opérateur == exige une égalité stricte sur la totalité du champ. Ici la difficulté vient du format : dans le Combined Log, la requête est encadrée par des guillemets doubles, donc le sixième champ vaut littéralement "POST avec son guillemet ouvrant collé. La séquence "\"POST" construit cette chaîne dans le programme awk, la barre oblique inverse protégeant le guillemet interne. Si vous cherchez une sous-chaîne plutôt qu'une valeur exacte, utilisez l'opérateur regex présenté juste après.

Fenêtre de terminal
# Requêtes POST seulement
awk '$6 == "\"POST" {print $1, $7, $9}' access.log
10.0.0.5 /login 401
10.0.0.5 /login 200

Les opérateurs ~ et !~ appliquent une expression régulière à un champ précis, ce que grep ne sait pas faire : grep 192.168 remonterait aussi une ligne où cette adresse apparaît dans l'URL ou l'agent utilisateur. Le motif se place entre barres obliques et n'a pas besoin d'être ancré par défaut, d'où le ^ dans le premier exemple pour exiger un début de champ. Notez les points échappés en \. : sans cela, le point de la regex accepte n'importe quel caractère.

Fenêtre de terminal
# IPs du réseau 192.168 seulement
awk '$1 ~ /^192\.168\./ {print $1, $9}' access.log
192.168.1.10 200
192.168.1.10 200
192.168.1.10 500
Fenêtre de terminal
# Exclure les requêtes de alice
awk '$3 !~ /alice/ {print $1, $3, $9}' access.log
10.0.0.5 bob 401
10.0.0.99 - 403
10.0.0.5 bob 200
172.16.0.3 charlie 404
  • ~ : le champ correspond au pattern
  • !~ : le champ ne correspond pas au pattern

BEGIN s'exécute avant la première ligne. END s'exécute après la dernière. Utilisés pour les en-têtes, les totaux et les résumés.

C'est le motif à retenir pour tout calcul d'agrégat : accumuler ligne par ligne dans une variable, puis afficher le résultat dans END. Deux détails comptent. awk initialise implicitement toute variable numérique à 0, il n'y a donc rien à déclarer avant la boucle. Et le programme s'étale ici sur plusieurs lignes à l'intérieur d'une même paire de guillemets simples : le shell ne rend la main qu'à la quote fermante, ce qui autorise cette mise en forme lisible. La séquence '\'' insère une apostrophe française dans le texte affiché sans casser cette quote.

Fenêtre de terminal
awk -F, 'NR == 1 { next }
{
total += $3 * $4
lignes++
}
END {
print "Lignes traitées :", lignes
print "Chiffre d'\''affaires total :", total, "€"
}' ventes.csv
Lignes traitées : 7
Chiffre d'affaires total : 29800 €
  • total += $3 * $4 : accumulation dans une variable globale
  • lignes++ : compteur de lignes traitées
  • Le bloc END est appelé une seule fois après toutes les lignes

awk supporte nativement les tableaux dont les clés sont des chaînes, l'outil parfait pour compter des occurrences par valeur.

Cette commande fait en une ligne ce qui demanderait un sort | uniq -c sur un champ préalablement extrait. La clé du tableau est la valeur lue dans le champ, pas un indice numérique : awk crée l'entrée à la première rencontre et l'incrémente ensuite. Résultat, le nombre de compteurs s'adapte tout seul aux valeurs présentes dans le fichier, sans que vous ayez à connaître à l'avance la liste des codes HTTP.

Fenêtre de terminal
awk '{codes[$9]++} END {for (c in codes) print c, codes[c]}' access.log | sort
200 3
401 1
403 1
404 1
500 1
  • codes[$9]++ : incrémente le compteur indexé par la valeur du code HTTP
  • for (c in codes) : itère sur toutes les clés du tableau
  • | sort : awk ne garantit pas l'ordre des clés, on trie à la fin

Voici le cas réel qui justifie d'apprendre les tableaux associatifs : repérer d'un coup d'œil quel client génère des erreurs. Le programme tient deux compteurs indexés sur la même clé, l'adresse IP, et les affiche côte à côte. La colonne à surveiller est la troisième : une IP avec beaucoup de requêtes et peu d'erreurs est un utilisateur normal, une IP avec un ratio proche de 1 ressemble à un balayage ou à un client cassé. Le sort final ne peut pas être remplacé par une option d'awk, l'ordre de parcours d'un tableau n'étant pas défini.

Fenêtre de terminal
awk '{ip[$1]++; if ($9 >= 400) err[$1]++}
END {
for (i in ip) printf "%-16s %6d %6d\n", i, ip[i], err[i]+0
}' access.log | sort -k2 -rn
192.168.1.10 3 1
10.0.0.5 2 1
172.16.0.3 1 1
10.0.0.99 1 1
  • Deux tableaux parallèles : ip[] pour le total, err[] pour les erreurs
  • err[i]+0 : si la clé n'existe pas, retourne 0 (pas d'erreur de type)
  • sort -k2 -rn : trier par deuxième colonne (requêtes), décroissant

print sépare les champs avec l'OFS (espace par défaut). printf donne un contrôle précis sur l'alignement et le format.

Fenêtre de terminal
awk -F, 'NR > 1 {printf "%-10s %-6s %6d €\n", $1, $2, $3*$4}' ventes.csv
laptop nord 6000 €
serveur sud 7000 €
switch nord 4500 €
laptop est 3600 €
serveur nord 3500 €
switch sud 3600 €
disque est 1600 €

Formats printf courants :

FormatEffet
%-10sChaîne, alignée à gauche, largeur 10
%10sChaîne, alignée à droite, largeur 10
%6dEntier, largeur 6
%.2fFlottant, 2 décimales
%sChaîne sans largeur fixe
OutilQuand l'utiliser
grepFiltrer des lignes par pattern texte, pas de raisonnement sur les champs
cutExtraire des colonnes à position fixe ou délimiteur simple, sans calcul
sedTransformer du texte, substituer des patterns, modifier in-place
awkFiltrer par champ, calculer, compter, reformater, créer des tableaux de synthèse
PythonLogique complexe, structures de données riches, fichiers JSON/YAML

La règle pratique : si vous avez besoin de raisonner sur la valeur d'un champ (pas juste extraire), commencez par awk.

La première ligne du tableau concerne le shell, pas awk : c'est de loin l'erreur la plus fréquente, parce que le message renvoyé parle de syntaxe awk alors que le programme a déjà été déformé par bash avant d'arriver à l'interpréteur. Les quatre suivantes tiennent au comportement d'awk lui-même et ne déclenchent aucune erreur : le résultat est simplement faux ou incomplet, ce qui est bien plus dangereux dans un script de production. La dernière, NR contre FNR, ne se manifeste que si vous passez plusieurs fichiers à la même commande.

PiègeSymptômeSolution
Guillemets dans le shell" dans le pattern awk interprété par bashToujours encadrer le programme awk avec des '...'
Confondre print et printfSaut de ligne manquant avec printfAjouter \n explicitement dans printf
Clé inexistante dans un tableauValeur vide affichée, et la clé est créée au passage (elle apparaît ensuite dans for (k in arr))Utiliser arr[k]+0 pour les entiers, arr[k]"" pour les chaînes ; tester avec if (k in arr), qui ne crée rien
Ordre des clés d'un tableauRésultat non triéPiper vers sort après le bloc END
NR vs FNR avec plusieurs fichiersNR continue de s'incrémenter entre fichiersUtiliser FNR pour le numéro de ligne dans le fichier courant

Vérifiez que l'essentiel de ce guide est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

6 questions
6 min.
70% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

  • $1, $NF, NR, les variables de base à connaître par cœur
  • -F: ou -F,, change le séparateur de champ
  • $3 >= 400, conditions numériques directes sur un champ
  • $1 ~ /regex/ et $1 !~ /regex/, filtrage par pattern sur un champ
  • tableau[$clé]++, compter des occurrences par valeur
  • BEGIN / END, avant la première ligne / après la dernière
  • printf "%-10s %6d\n", formater une sortie en tableau aligné
  • | sort, toujours trier après for (k in tableau) si l'ordre compte
  • Référence awk : Variables internes, fonctions, getline, OFMT, CONVFMT, le condensé complet des options awk.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn