Linux sans magie #27 — awk : comprendre les champs, les enregistrements et les premiers traitements de texte
Comprendre les bases de awk : records, champs, séparateurs, filtres, print, NF, NR et premiers calculs pour analyser des données texte sous Linux.
Après grep, cut, sort, uniq, wc et sed, nous arrivons à un outil capable de faire davantage que sélectionner ou remplacer du texte : awk.
awk lit des données ligne par ligne, découpe chaque ligne en champs, permet de tester des conditions et exécute des actions sur les lignes qui correspondent.
Son intérêt apparaît rapidement dès que l’on veut répondre à des questions comme :
- afficher certaines colonnes ;
- filtrer selon la valeur d’un champ ;
- compter des enregistrements ;
- additionner des valeurs numériques ;
- reformater une sortie ;
- combiner plusieurs de ces opérations dans une seule commande.
L’objectif de cet article est de comprendre le modèle mental de base de awk, pas d’en faire un cours complet de programmation.
Le modèle mental : records, champs et actions
Par défaut, awk lit son entrée sous forme de records. Dans les cas simples, un record correspond à une ligne.
Chaque record est ensuite découpé en fields, c’est-à-dire en champs.
Prenons cette ligne :
alice 12 admin
Pour awk :
$0 → alice 12 admin
$1 → alice
$2 → 12
$3 → admin
$0 représente le record complet.
$1, $2, $3 représentent les champs individuels.
La variable prédéfinie NF contient le nombre de champs du record courant.
Dans cet exemple :
NF → 3
$NF → admin
$NF désigne donc le dernier champ, quel que soit le nombre de champs présents sur la ligne.
Premier exemple avec print
Créons un petit jeu de données :
mkdir -p /tmp/linux-sans-magie-27
cd /tmp/linux-sans-magie-27
cat > utilisateurs.txt <<'EOF'
alice 12 admin
bob 7 user
claire 19 admin
david 3 user
emma 15 user
EOF
Pour afficher le premier champ :
awk '{ print $1 }' utilisateurs.txt
Résultat attendu :
alice
bob
claire
david
emma
La partie entre accolades est l’action exécutée pour chaque record.
Ici, il n’y a pas de condition : toutes les lignes sont donc traitées.
Afficher plusieurs champs
Pour afficher le premier et le troisième champ :
awk '{ print $1, $3 }' utilisateurs.txt
Résultat :
alice admin
bob user
claire admin
david user
emma user
Avec print, les éléments séparés par des virgules sont affichés avec le séparateur de sortie par défaut.
Pour ajouter du texte fixe :
awk '{ print "utilisateur:", $1, "role:", $3 }' utilisateurs.txt
Résultat :
utilisateur: alice role: admin
utilisateur: bob role: user
utilisateur: claire role: admin
utilisateur: david role: user
utilisateur: emma role: user
Cette capacité rend awk pratique pour produire rapidement une sortie lisible.
Utiliser $0, NF et $NF
Pour afficher la ligne complète :
awk '{ print $0 }' utilisateurs.txt
Cela peut sembler inutile, mais $0 devient très pratique quand il est associé à une condition.
Pour afficher le nombre de champs de chaque ligne :
awk '{ print NF, $0 }' utilisateurs.txt
Résultat attendu :
3 alice 12 admin
3 bob 7 user
3 claire 19 admin
3 david 3 user
3 emma 15 user
Pour afficher uniquement le dernier champ :
awk '{ print $NF }' utilisateurs.txt
Résultat :
admin
user
admin
user
user
$NF évite d’avoir à connaître à l’avance le numéro du dernier champ.
Filtrer avec une condition
La structure générale la plus importante à retenir est :
condition { action }
Pour afficher seulement les lignes dont le troisième champ vaut admin :
awk '$3 == "admin" { print $0 }' utilisateurs.txt
Résultat :
alice 12 admin
claire 19 admin
On peut simplifier l’action :
awk '$3 == "admin" { print }' utilisateurs.txt
Un print sans argument affiche le record complet.
Comparer des valeurs numériques
Le deuxième champ de notre fichier contient une valeur numérique.
Pour afficher les utilisateurs dont cette valeur est supérieure ou égale à 10 :
awk '$2 >= 10 { print $1, $2 }' utilisateurs.txt
Résultat attendu :
alice 12
claire 19
emma 15
La condition est évaluée pour chaque record.
Si elle est vraie, l’action entre accolades est exécutée.
Utiliser une expression régulière comme filtre
awk peut aussi filtrer selon une expression régulière.
Pour afficher les lignes contenant admin :
awk '/admin/ { print }' utilisateurs.txt
Cette forme recherche le motif dans le record complet, c’est-à-dire dans $0.
Pour cibler uniquement un champ, on peut utiliser l’opérateur ~ :
awk '$3 ~ /^adm/' utilisateurs.txt
Résultat :
alice 12 admin
claire 19 admin
Ici, seul le troisième champ est comparé à l’expression régulière.
Compter avec NR
NR est une variable prédéfinie qui contient le nombre de records lus depuis le début du traitement.
Pour numéroter les lignes :
awk '{ print NR, $0 }' utilisateurs.txt
Résultat :
1 alice 12 admin
2 bob 7 user
3 claire 19 admin
4 david 3 user
5 emma 15 user
Pour connaître le nombre total de lignes lues :
awk 'END { print NR }' utilisateurs.txt
Le bloc END est exécuté une fois, après lecture de toutes les données.
Résultat :
5
Premier calcul : additionner une colonne
awk peut mémoriser des valeurs dans des variables.
Pour additionner le deuxième champ :
awk '{ total += $2 } END { print total }' utilisateurs.txt
Résultat :
56
Le raisonnement est le suivant :
pour chaque ligne :
total = total + $2
à la fin :
afficher total
Cette logique permet déjà de produire des statistiques simples sans écrire un script plus lourd.
Calculer une moyenne simple
On peut combiner la somme et le nombre de records :
awk '{ total += $2 } END { print total / NR }' utilisateurs.txt
Résultat attendu :
11.2
Ce calcul suppose que toutes les lignes traitées contiennent bien une valeur numérique exploitable dans le deuxième champ.
Sur un fichier réel, il faut donc vérifier la structure des données avant d’interpréter le résultat.
Choisir un séparateur avec -F
Jusqu’ici, les champs étaient séparés par des espaces.
Prenons maintenant un fichier séparé par des deux-points :
cat > services.txt <<'EOF'
web:prod:3
api:test:2
db:prod:1
cache:test:4
EOF
Pour indiquer que : est le séparateur :
awk -F':' '{ print $1, $2, $3 }' services.txt
Résultat :
web prod 3
api test 2
db prod 1
cache test 4
L’option -F définit le séparateur utilisé pour découper chaque record en champs.
Pour afficher uniquement les services de production :
awk -F':' '$2 == "prod" { print $1 }' services.txt
Résultat :
web
db
Utiliser BEGIN pour préparer le traitement
Le bloc BEGIN est exécuté avant la lecture du premier record.
Il peut servir à initialiser des variables ou à produire un en-tête.
Exemple :
awk -F':' 'BEGIN { print "SERVICES PROD" } $2 == "prod" { print $1 }' services.txt
Résultat :
SERVICES PROD
web
db
Pour des programmes plus longs, cette structure devient vite plus lisible qu’une succession de commandes shell.
Lab : produire un petit rapport
Utilisons le fichier suivant :
cat > consommation.txt <<'EOF'
web:prod:12
api:prod:8
db:prod:20
cache:test:4
worker:test:6
EOF
Nous voulons :
- sélectionner les services en production ;
- afficher leur nom et leur valeur ;
- additionner leur valeur ;
- afficher le total à la fin.
Commande :
awk -F':' '
$2 == "prod" {
print $1, $3
total += $3
}
END {
print "total:", total
}
' consommation.txt
Résultat attendu :
web 12
api 8
db 20
total: 40
Ce petit exemple montre pourquoi awk est plus qu’un simple outil d’extraction de colonnes.
Il associe :
- une condition ;
- une action ;
- des champs ;
- une variable ;
- un calcul ;
- un traitement final avec
END.
Construire progressivement une commande awk
Comme pour les pipelines vus précédemment, évitez d’écrire directement une expression complexe.
Commencez par vérifier le découpage :
awk -F':' '{ print $1, $2, $3 }' consommation.txt
Puis vérifiez le filtre :
awk -F':' '$2 == "prod" { print $0 }' consommation.txt
Puis affichez les champs utiles :
awk -F':' '$2 == "prod" { print $1, $3 }' consommation.txt
Enfin, ajoutez le calcul :
awk -F':' '$2 == "prod" { total += $3 } END { print total }' consommation.txt
Cette progression permet de repérer rapidement l’étape qui ne produit pas le résultat attendu.
awk dans un pipeline
awk peut lire depuis stdin.
Exemple :
grep ':prod:' consommation.txt | awk -F':' '{ print $1, $3 }'
Cette commande fonctionne.
Mais awk sait déjà appliquer lui-même une condition :
awk -F':' '$2 == "prod" { print $1, $3 }' consommation.txt
Dans ce cas précis, la seconde forme est plus directe.
Cela ne signifie pas qu’il faut supprimer les pipelines. Le bon choix dépend de la lisibilité du traitement et de la responsabilité de chaque outil.
Bonnes pratiques
Commencez par identifier clairement la structure des données : une ligne correspond-elle bien à un record ? Quel est le séparateur ? Les mêmes champs sont-ils présents partout ?
Utilisez $0 lorsque vous avez besoin de la ligne complète et les références $1, $2, $NF lorsque vous travaillez sur des champs précis.
Vérifiez NF si la structure du fichier peut être irrégulière.
Par exemple, pour afficher uniquement les lignes possédant trois champs :
awk 'NF == 3 { print }' utilisateurs.txt
Pour des formats délimités, définissez explicitement le séparateur avec -F.
Gardez aussi les expressions awk courtes tant que vous travaillez directement sur la ligne de commande. Lorsqu’un programme commence à contenir de nombreuses conditions, fonctions ou blocs, un fichier de script awk peut devenir plus maintenable.
Sécurité
Les exemples de cet article lisent des fichiers et écrivent leurs résultats sur stdout.
Ils ne modifient pas directement les fichiers sources.
Comme avec les autres outils shell, une redirection peut toutefois créer ou remplacer un fichier :
awk -F':' '$2 == "prod" { print }' services.txt > services-prod.txt
Avant de rediriger vers un fichier existant important, vérifiez toujours le nom de destination.
Il faut également être prudent avec les données contenant des secrets, des identifiants ou des informations personnelles : extraire ou reformater un fichier avec awk peut rendre ces données visibles dans un terminal, un log ou un nouveau fichier.
Coûts
awk est ici utilisé comme outil local de traitement de texte.
Les manipulations présentées n’introduisent pas de coût de service ou de ressource cloud.
Limites et points de vigilance
Cet article reste volontairement sur les bases.
Nous ne couvrons pas encore :
- les tableaux associatifs ;
- les boucles ;
- les fonctions utilisateur ;
printfen détail ;- les conditions complexes ;
- les traitements multifichiers avancés ;
FNR;- les variables
FS,OFS,RSetORSen profondeur ; - les différences entre les fonctionnalités POSIX
awket les extensions spécifiques à GNUgawk; - les formats structurés complexes comme JSON ou YAML.
Il faut aussi éviter de considérer awk comme un parseur universel.
Un fichier CSV contenant des champs entre guillemets, des séparateurs échappés ou des retours à la ligne internes nécessite un traitement adapté à la structure réelle du format.
Rollback ou nettoyage
Le lab ne modifie pas les fichiers sources.
Pour supprimer tous les fichiers créés pendant l’exercice :
rm -rf /tmp/linux-sans-magie-27
Cette commande cible uniquement le répertoire temporaire créé explicitement pour ce lab.
Références officielles
- GNU Awk User’s Guide — GNU Project
- GNU Awk User’s Guide — Examining Fields — GNU Project
- GNU Awk User’s Guide — Field Separators — GNU Project
- GNU Awk User’s Guide — Patterns, Actions, and Variables — GNU Project
- GNU Awk User’s Guide — The print Statement — GNU Project
Conclusion
Le modèle de base de awk tient en quelques notions :
record → une unité d’entrée, généralement une ligne
$0 → le record complet
$1, $2... → les champs
NF → le nombre de champs
$NF → le dernier champ
pattern → condition
{ action } → traitement exécuté
NR → nombre de records lus
BEGIN / END → actions avant et après le traitement
Avec ces éléments, awk permet déjà de filtrer, extraire, compter et calculer dans un seul traitement lisible.
C’est une base suffisante avant d’aborder ses fonctions plus avancées.
