Avant propos
Cette page ne se veut certainement pas complete, ni une reference, ni un cours, ni quoi que ce soit. Ce n’est qu’une tres modeste introduction, juste pour donner envie et faire decouvrir le potentiel de cette commande.
Qu'est ce que awk ?
AWK est tout a la fois un langage et un programme portant ce meme nom. Le contexte permet en general de savoir de quoi on parle.
À quoi sert awk ?
Awk est une sorte de grep programmable; donc awk traite des fichiers texte . Mais en plus, de selectionner simplement des lignes, awk peut aussi travailler sur les colonnes tres simplement. C’est meme l’outil ideal quand on veut travailler sur les colonnes d’un fichier texte. Mais awk est bien plus que ca, comme il y a un langage assez riche, on peut faire des calculs, avant de ce commencer a lire les fichiers texte (initialiser tout un tas de trucs et de machins), faire des calculs apres avoir lu toutes les lignes de tous les fichiers, pour generer un rapport par exemple.
Si awk est tres complexe – plusieurs livres lui sont consacres – il peut aussi etre infiniment simple et pratique pour un travail en ligne de commande au quotidien. Les exemples que nous donnons ci apres ne sont qu’une microscopique partie de la partie emergee de l’iceberg.
Un peu d'histoire
Aho Weinberger Kernighan se reunissent dans un bateau et tombent a l’eau Que reste-t-il ? AWK. On distinguera awk, la commande originale, encore trouvable sur des systemes dont l’historique est plus long que celui des jeunes et souvent plus familier GNU/Linux, tel que Solaris par exemple, du new awk (<CODE>nawk</CODE> sur ces systemes), (version normalisee POSIX ?) arrivee un peu plus tard sur le marche.
Les implementations GNU/Linux de awk, sont en fait des new awk. La presente page ne parle que de new awk. Conclusion, si vous desirez essayer les exemples, essayez d’abord avec une hypothetique commande nawk, si nawk n’existe pas, essayez avec awk (ce sera sans doute un new awk).
Derniere petite chose. On trouve en general la commande awk dans /usr/bin. Certains systemes GNU/linux ont eu la mauvaise idee de la mettre dans /bin. Pas tres grave, pensez vous car en general, les deux sont dans le PATH... Oui, mais tout de meme, on peut faire des scripts en awk, et le shebang (!# /usr/bin/awk -f) devient faux, et le script inutilisable si le binaire n’est pas la où on l’attend.
Les implémentations de awk
Tout depend, et, pour reprendre la section precedente, on peut avoir a faire au awk d’origine ou a un nawk.
il y a donc :
- awk
- nawk (POSIX ?)
- mawk : tres bonne implementation de nawk, utilisee par defaut sur les systemes Debian GNU/Linux
- gawk : encore une GNUrie. D’aucuns lui trouvent certains avantages, la possibilite de ne pas tenir compte de la casse (majuscule/minuscule), internationnalisation etc... Pleins d’extensions “non standard”, gros, lent, mais certainement innovant.
- sans doute pleins d’autres, faites nous parvenir vos decouvertes.
Les rapports aux "autres" programmes de manipulations de données textes
sed / awk
- si vous voulez traiter des colonnes, ou des paragraphes entiers, utilisez si vous voulez juste remplacer une chaine par une autre dans un
texte, la coutume est d’utiliser sed. Si vous voulez extraire une ligne ou deux et faire un peu de modification de texte :
- si le critere de selection de ligne est immediat (presence
d’un marqueur) utilisez sed
- Si le critere est complexe (la somme des nombres situes en
colonne 4 et 7 est superieure a 25), utilisez awk d’une maniere general, sed n’a qu’une seule variable – et seuls quelques vieux sages savent s’en servir –, donc, si les criteres font intervenir une memorisation quelconque, ou un calcul, utiliser awk
grep
En utilisation courante, c’est egal. grep est plus direct et est l’outil classique. Disons que bla | grep toto indique clairement ce qu’on fait, alors que bla | nawk /toto/ ... necessite qu’on reflechisse a ce qui est fait par awk (car il peut faire n’importe quoi)
La version GNU de grep a bien des avantages, comme la coloration de la chaine recherchee. De plus grep peut afficher facilement des lignes de contexte avant et apres la ligne recherchée (options -B -A et -C), ce qui nécessite un programme complet avec awk.
Perl
Perl est une monster application. Son initialisation est tres longue, et tres couteuse (ouverture de nombreux fichiers, recherche de module dans un grand nombre de repertoires differents, etc...) Si c’est pour remplacer trois lettres dans une ligne, utiliser sed Si c’est pour extraire trois colonnes, utiliser awk
Serieusement, perl n’est pas comparable et ne joue pas dans la meme cour. Cela etant dit, dans un domaine bien particulier, et selon les dire du grand Larry lui meme, awk est superieur a perl. Selon la page de manuel de perlvar :
Remember: the value of $/ is a string, not a regex. awk has to be better for something. :-)
Exécuter awk
Non, nous ne parlons pas ici de mise a mort, mais de la maniere de se vervir de awk. L’invocation standard est :
awk 'commande awk' fichier fichier...
et cela signifie : executer la ‘commande awk’ pour l’ensemble des fichiers exemple :
awk '{ print NR, $0 }' *.txt
Si aucun fichier n’est donne, awk lit son entree standard, ce qui fait qu’on peut l’utiliser dans les pipes, en filtre
exemple :
ls -l | awk '{ print ; s += $5 } END { print "total =", s }'
Enfin, on peut definir des variables awk avant d’executer quoi que ce soit.
ls | awk -v x=coucou '{ print x, $0 }'
Des exemples simples
Ces exemples simples permettent d’approcher doucement awk. L’idee de awk est que les lignes sont composees de colonnes; la definition de la colonne etant la definition naturelle.
- extraire une ligne contenant un modele donne (une expression rationnelle)
grep 'regexp'
nawk '/regexp/'- extraire la colonne 4
nawk '{ print $4 }'- extraire la derniere colonne
nawk '{ print $NF }'- extraire la colonne 7 et 5, si le nombre en colonne 3 est plus grand que pi
nawk '$3 > 3.141592654 { print $7, $5 }'- lignes paires :
nawk '!(FNR % 2)'
- lignes impaires :
nawk 'FNR % 2'
Si votre fichier a des colonnes separees par autre chose que des blancs, par exemple des : comme le fichier /etc/passwd, utiliser l’option -F exemple :
nawk -F : '$3 > 150 { print $1, $3, $4, $7 }' /etc/passwd
Mais pourquoi utilise-t-on des { } de temps en temps mais pas toujours ? C’est parceque un script awk est compose de test et d’actions. Les actions son entourees de { } , pas les tests. Cela est explique dans la section suivante.
Langage awk
Entrons enfin dans le vif du sujet.
Grosso modo le langage awk est du C (kernighan oblige), mais interprete et ou les variables peuvent etre de n’importe quel type. (entier, flotant ou chaine)
on note les chaines entre ““, comme dans x=”coucou”
les entiers et les flottants s’ecrivent normalement :
- x=4
- pi=3.14159
- z=1.5e12
Attention a vos reglages internationnaux. Si votre variable LC_ALL ou LC_NUMERIC vaut fr_FR ou un truc du genre, vous allez vous retrouver avec des nombres a virgule oui, a virgule, pas a point (.), et cela sera sans doute la cause de bien des maux.
awk lit des enregistrements (des lignes) qu’il decoupe en champs (en colonnes). la ligne de base est aussi conservee sans modification. On peut changer ce reglage grace a des variables internes – dont les noms sont toujours en majuscule – Ici il s’agit des variable RS (Record Separator) et FS (Field Separator).
Pour lire des paragraphes entiers et decouper en ligne, il faut que RS soit vide et que FS contienne un retour chariot S soit vide et que FS contienne un retour chariot exemple :
nawk 'BEGIN { RS="" ; FS = "\n" } { printf "enregistrement %3d, nombre de champs %d\n, %s\n", NR, NF,\ $0 }'
NB :
On utilisera maintenant le terme ligne pour enregistrement et le terme colonne pour champ, car c’est ce qu’on\ fait le plus souvent. Ne perdez pas de vue cependant que cela peut etre change.
Structure d'un script
Un script en awk est une suite de paire : test action
Pour chaque ligne, un test est effectue, et s’il resussi, l’action associee est effectuee. Si l’action precedente n’a pas provoque la fin du programme (exit) ou le passage a une nouvelle ligne (next), le test suivant est effectue et son action associee est executee si necessaire etc...
- Ne pas mettre de test est considere comme un test qui reussi toujours
- Ne pas mettre d’action provoque l’action par defaut si le test reussi : l affichage de la ligne.
- omettre un test et une action (faire un script vide) ne fait rien.
- le test BEGIN est vrai avant de commencer la lecture de quoi que ce soit on l’utilise pour initialiser
- les test END est vrai apres que la derniere ligne soit lue. on l’utilise generer des rapport, afficher des resultat lentements collectes au cours de la lecture...
Les tests peuvent etre n’importe quelle expression qui s’evalue numeriquement ou “chainement” ou une expression rationnelle entre / ou toute combinaison de tout cela grace au operateur et-logique ou-logique etc...
- une expression numerique est “vraie” si elle est non nulle
- une expression chaine est “vraie” si elle est non vide
- une /expression rationnelle/ est “vraie” si elle concorde avec la ligne en cours de traitement
ainsi les scripts suivants affichent toutes les lignes de leur entree :
nawk 1
nawk '"a"'
et ceux la n’affichent rien :
nawk 0
nawk '""'
La magie de awk
une part importante de la magie de awk reside dans son operateur $ Operateur qu’il ne faut pas confondre avec le $ du shell. D’ailleurs les scripts en awk se mettent presque toujours entre ' ' pas entre " "
Cet operateur est le selecteur de colonne, et il attend un entier.
$0 correspond a la ligne telle qu’elle a ete lue.
exemple : afficher le dernier mot de la ligne
awk '{ print $NF }'
NF est une autre variable magique de awk, elle contient le nombre de colonnes, et est mise a jour a la lecture de chaque nouvelle ligne.
afficher l’avant dernier mot de la ligne :
awk '{ print $(NF - 1) }'
Une autre part de magie reside dans les tableaux. Les tableaux sont des tableaux associatifs (ce qu’on appelle dictionnaire en Postscript, Hash en perl). L’indice peut etre n’importe quoi. cela permet d’associer une valeur a une clef, comme dans l’exemple suivant :
awk '
{
for( i = 1; i <= NF; ++i ) {
frequence[ $i ] ++
}
END {
for( i in frequence ) {
printf "frequence[ %s ] = %s\n", i, frequence[i]
}
}
' *.txt
autre exemple :
nawk '
/^From:/ {
de[$2]++
}
END {
for( i in de ) {
printf "%s a ecrit %d messages\n", i , de[i]
}
}
' /var/mail/$USER
Historique du document
- Première version de Christophe Martin (10 Mars 2006)



