Le langage de programmation awk
Présentation
Le langage de programmation awk est un outil adapté au traitement de fichiers textes. Il permet d’effectuer des actions sur des enregistrements de données éventuellement structurés en champs. Le nom "awk" a pour origine les initiales du nom de chacun de ses auteurs : Aho, Weinberger et Kernighan. Ce chapitre présente les principales fonctionnalités des versions récentes du langage awk, appelées nawk (new awk) sur certaines plates-formes Unix. Sous Linux, la commande awk est un lien symbolique vers l’interpréteur gawk (GNU awk).
1. Syntaxe
awk [-F] '{action-awk}' [ fic1 ... ficn ]
awk [-F] -f script-awk [ fic1 ... ficn ]
La commande awk prend en argument la liste des fichiers à traiter. En l’absence de noms de fichiers sur la ligne de commande, awk travaille sur les données arrivant sur son entrée standard. Cette commande peut donc être placée derrière un tube de communication.
2. Version de gawk
La version de gawk s’obtient en utilisant l’option --version.
Exemple
$ gawk --version
GNU Awk 4.1.3, API: 1.1
3. Variables spéciales
a. Variables prédéfinies dès le lancement de awk
Le tableau suivant présente les principales variables internes du langage awk présentes en mémoire dès le lancement de la commande. La valeur de ces variables peut éventuellement être modifiée en fonction de la structure des données à traiter.
Nom de la variable |
Valeur par défaut |
Rôle de la variable |
RS |
Newline (\n) |
Record Separator : caractère séparateur d’enregistrement (lignes). |
FS |
Suite d’espaces et/ou tabulations |
Field Separator : caractères séparateurs de champ. |
OFS |
Espace |
Output Field Separator : séparateur de champ utilisé... |
Opérateurs
Le tableau suivant regroupe les opérateurs disponibles dans le langage.
Opérateur |
Arité |
Signification |
Opérateurs arithmétiques |
||
+ |
Binaire |
Addition |
- |
Binaire |
Soustraction |
* |
Binaire |
Multiplication |
/ |
Binaire |
Division |
% |
Binaire |
Modulo |
ˆ |
Binaire |
Exponentiation |
++ |
Unaire |
Incrémentation d’une variable d’une unité |
-- |
Unaire |
Décrémentation d’une variable d’une unité |
+= |
Binaire |
x+=y est équivalent à x=x+y |
-= |
Binaire |
x-=y est équivalent à x=x-y |
*= |
Binaire |
x*=y est équivalent à x=x*y |
/= |
Binaire |
x/=y est équivalent à x=x/y |
%= |
Binaire |
x%=y est équivalent à x=x%y |
ˆ= |
Binaire |
xˆ=y est équivalent à x=xˆy |
Opérateurs de tests |
||
< |
Binaire |
Inférieur |
> |
Binaire |
Supérieur |
<= |
Binaire |
Inférieur ou égal |
>= |
Binaire |
Supérieur ou égal |
== |
Binaire |
Test d’égalité |
!= |
Binaire |
Test d’inégalité |
~ |
Binaire |
Correspondance avec une expression régulière |
!~ |
Binaire |
Non correspondance avec une expression régulière |
Opérateurs logiques |
||
! |
Binaire |
Négation |
&& |
Binaire |
Et logique |
|| |
Binaire |
Ou logique |
Divers |
||
= |
Binaire |
Affectation |
e1 ? e2 : e3 |
Ternaire |
L’expression globale vaut e2 si e1 est vrai, e3 dans le cas contraire |
e1 e2 (opérateur espace) |
Binaire |
Concaténation de e1 et e2 |
Exemple de concaténation
Concaténation (opérateur espace) de la variable nom et de la variable prenom dans la variable nomPrenom :
$ awk 'BEGIN { nom="Durand" ; prenom="Michel" ; nomPrenom=nom prenom ;
print nomPrenom}'
DurandMichel
Même chose en concaténant le caractère espace entre le nom et le prénom :
$ awk 'BEGIN { nom="Durand" ; prenom="Michel"...
La fonction printf
awk propose la fonction intégrée printf similaire à celle du langage C. Elle permet de formater les affichages.
printf ("chaine",expr1,expr2, ..., exprn)
chaine représente la chaîne qui sera affichée à l’écran. Elle peut contenir des formats qui seront substitués par la valeur des expressions citées à sa suite. Il doit y avoir autant de formats que d’expressions.
Exemples de formats couramment utilisés
%20s |
Affichage d’une chaîne (string) sur 20 positions (cadrage à droite par défaut). |
%-20s |
Affichage d’une chaîne (string) sur 20 positions avec cadrage à gauche. |
%3d |
Affichage d’un entier (décimal) sur 3 positions (cadrage à droite). |
%03d |
Affichage d’un entier (décimal) sur 3 positions (cadrage à droite) complété par des 0 à gauche. |
%-3d |
Affichage d’un entier (décimal) sur 3 positions (cadrage à gauche). |
%+3d |
Affichage d’un entier (décimal) sur 3 positions (cadrage à droite) avec affichage systématique du signe (un nombre négatif est toujours affiché avec son signe). |
%10.2f |
Affichage d’un nombre flottant sur 10 positions dont 2 décimales. |
%+010.2f |
Affichage d’un nombre flottant sur 10 positions dont 2 décimales, cadrage à droite, affichage systématique du signe, complétion par des zéros à gauche. |
Des exemples d’utilisation de la fonction printf sont donnés dans la suite de ce chapitre.
Redirections
Il est possible de rediriger les sorties du script vers un fichier ou vers une commande du système d’exploitation.
Syntaxe
|
Au premier appel, ouverture en mode "écrasement", puis écriture. Les écritures ultérieures se font à la suite de la ligne précédente. L’expression "fichier" vaudra "/dev/stderr" pour écrire sur la sortie d’erreur standard. |
|
Au premier appel, ouverture en mode "ajout", puis écriture. Les écritures ultérieures se font à la suite de la ligne précédente. L’expression "fichier" vaudra "/dev/stderr" pour écrire sur la sortie d’erreur standard. |
|
Le résultat de l’instruction print est transmise sur l’entrée standard de la commande par l’intermédiaire d’un tube. |
Premier exemple
Ouverture en mode écrasement :
$ nl redirect1.awk
1 BEGIN {
2 nomfic = "/tmp/fic.txt"
3 print "Ligne 1" > nomfic
4 print "Ligne 2" > nomfic
5 print "Ligne 3" > nomfic
6 close(nomfic)
7 }
Exécution :
$ date > /tmp/fic.txt # création d'un fichier non vide
$ cat /tmp/fic.txt
mer. jan 26 14:14:32 CET 2022
$ awk -f redirect1.awk # Exécution du script awk
$ cat /tmp/fic.txt # Le contenu précédent a été écrasé
Ligne 1
Ligne 2
Ligne 3
$
Deuxième exemple
Ouverture en mode ajout :
$ nl redirect2.awk
1 BEGIN { ...
Lecture de la ligne suivante : next
L’instruction next interrompt le traitement de la ligne courante et déclenche la lecture de la ligne suivante, sur laquelle le traitement intégral sera appliqué.
Exemple
Le script region1.awk réaffiche le fichier tel3.txt en ajoutant un champ avant le téléphone contenant la valeur RP pour les clients localisés en région parisienne et REGION pour les clients localisés en province.
$ nl region1.awk
1 BEGIN {
2 FS="|"
3 }
4 $3 ~ /^(7[578]|9[1-5])/ {
5 printf ("%s|%s|%s|%s|RP|%s\n",$1,$2,$3,$4,$5)
6 # Saut à l'enregistrement suivant
7 next
8 }
9 {
10 printf ("%s|%s|%s|%s|REGION|%s\n",$1,$2,$3,$4,$5)
11 }
Exécution :
$ awk -f region1.awk tel3.txt
Joyeux Giselle|12. rue de la Source|89290|Vaux|REGION|03.45.26.28.47
Dehaut Olivier|3 rue de Pussenval|75020|Paris|RP|01.78.25.96.78
Karama Josette|256 rue de la tempete|56100|Lorient|REGION|02.85.26.45.58
Zanouri Joel|45/48 boulevard du Gard|56100|lorient|REGION|02/85/56/45/58
Gron Pierre|89-90 rue du chateau|38350|La Mure|REGION|04.78.21.23.69
Grival Zoe|3, rue du chateau|38350|La Mure|REGION|04.78.21.78.69
$
Lignes 4 à 8 : la première section intermédiaire traite les codes postaux d’Ile de France. Dès que le traitement est terminé, l’instruction next redémarre...
Structures de contrôle
awk propose des structures de contrôle que l’on retrouve classiquement dans les langages de programmation. La syntaxe est héritée du langage C.
1. if
La partie else est facultative.
Syntaxe
if (condition) {
instruction
...
}
else {
instruction
...
}
Lorsqu’une seule instruction est présente, les accolades sont facultatives :
if (condition)
instruction
else
instruction
2. switch
gawk |
La structure de contrôle switch (l’équivalent en shell est la structure case) permet également de faire des tests.
La structure de contrôle switch est disponible en standard à partir de la version 4 de gawk (dans les versions supérieures à 3.1.3 et inférieures à 4, switch est disponible si gawk est compilé avec l’option --enable-switch).
Syntaxe
switch (expression) {
case valeur|expression-reguliere :
instruction
instruction
...
break
case valeur|expression-reguliere :
instruction
instruction
...
break
[ default :
instruction
instruction
break ]
}
Exemple
Le programme switch.gawk teste en ligne 6 le code postal ($3) présent dans le fichier tel3.txt. La valeur de la variable $3 est comparée aux différents cas (lignes 7, 10 et 13). Dès que l’un des cas est vrai, le switch se termine. Chaque...
Terminer un script
L’instruction exit permet à tout moment de terminer un script en retournant un statut au système d’exploitation.
Exemple
{
if ( NF < 3) exit 1 ; # Fin du script avec statut faux
. . .
. . .
}
END{
exit 0 # Fin du script avec statut vrai
}
Tableaux
Les éléments d’un tableau peuvent être indicés par un nombre ou par une chaîne de caractères. Cet indice est toujours vu comme une chaîne de caractères car dans le langage awk, tous les tableaux sont associatifs. Nous distinguerons néanmoins les deux cas de figure.
1. Tableaux indicés par un nombre
L’indice de départ est au choix du développeur.
Exemple
Ce script initialise un élément de tableau à chaque nouvel enregistrement traité. Le fichier traité est tel3.txt. Chaque élément représente le nom d’un client. Ce tableau est indicé à partir de 1 :
$ nl tab.awk
1 # Section BEGIN
2 BEGIN {
3 FS="|"
4 }
5 # Tableau stockant les noms des clients
6 {
7 client[NR]=$1
8 }
9 # Section END
10 END {
11 # Affichage du tableau
12 for (indice=1 ; indice <= NR ; indice++) {
13 printf("Client no %4d => %-20s\n",indice, client[indice]);
14 }
15 }
$
Résultat de l’exécution :
$ awk -f tab.awk tel3.txt
Client no 1 => Joyeux Giselle
Client no 2 => Dehaut Olivier
Client no 3 => Karama Josette
Client no 4 => Zanouri Joel
Client no 5 => Gron Pierre ...
Tableaux multidimensionnels
1. Simulation de tableaux multidimensionnels
Exception faite de gawk à partir de la version 4, les vrais tableaux multidimensionnels n’existent pas. Néanmoins, un mécanisme permet de simuler le fonctionnement d’un tableau multidimensionnel.
Exemple
$ nl tab2d.awk
1 BEGIN {
2 # Clé unique
3 tab[0,"nom"] = "Petit"
4 tab[0,"cp"] = "75001"
5 tab[1,"nom"] = "Dupont"
6 tab[1,"cp"] = "89000"
7
8 for (i=0; i<=1; i++) {
9 print "Indice " i " : "
10 print "Nom : " tab[i,"nom"]
11 print "CP : " tab[i,"cp"]
12 print "------------"
13 }
14
15 for (cle in tab) {
16 print "Clé : --" cle "-- Valeur => " tab[cle]
17 }
18 }
En réalité, la clé [0,"nom"] est stockée en interne sous forme d’une seule clé "0\034nom". La valeur \034 est le caractère séparateur (en mémoire) et est contenue dans la variable SUBSEP ; cette valeur est matérialisée par une virgule entre les crochets.
En ligne 16, la boucle affiche la valeur de la clé. En regardant le résultat ci-dessous, nous...
Les arguments de la ligne de commande
Les variables ARGV et ARGC
awk fournit un mécanisme qui permet de passer des arguments à un script au moment de son appel. Les variables ARGC et ARGV sont initialisées par awk et permettent de traiter les valeurs passées sur la ligne de commande. ARGV est un tableau qui contient les arguments reçus, hors options awk de la ligne de commande (ci-dessous -f arg.awk). ARGV[0] représente le nom de la commande (donc awk). Les valeurs contenues dans les postes suivants du tableau ARGV seront considérés comme les noms de fichier à traiter par les sections intermédiaires. ARGC contient le nombre d’arguments reçus. ARGV et ARGC sont disponibles dans toutes les sections du script.
Exemple
$ nl arg.awk
1 #! /bin/awk
2 BEGIN{
3 print "ARGC = " , ARGC
4 for (i=0;i<ARGC;i++) {
5 printf("ARGV[%d] = %s\n",i, ARGV[i])
6 }
7 }
$
$ awk -f arg.awk agenda.txt tel3.txt
ARGC = 3
ARGV[0] = awk
ARGV[1] = agenda.txt
ARGV[2] = tel3.txt
Transmettre des variables au script AWK à partir de la ligne de commande (versions actuelles de awk)
Sur les versions actuelles de awk (nawk,gawk), l’option -v est prévue pour initialiser des variables à partir de la ligne de commande. Ces variables seront disponibles dans toutes les sections du script (BEGIN, END et sections intermédiaires).
Syntaxe
$ awk -f script.awk -v var1=valeur1 [ -v var2=valeur2 ... ]
[fichier_a_traiter]
Exemple
Le script agenda1.awk permet de rechercher des informations dans le fichier agenda.txt. L’utilisateur...
Fonctions intégrées
Le langage awk dispose de fonctions intégrées.
1. Fonctions travaillant sur les chaînes
Fonction |
Rôle |
gsub(er,remp,[ch]) |
Remplace dans la chaîne "ch" chaque occurrence correspondant à l’expression régulière "er" par la chaîne "remp". Retourne le nombre de substitutions. Par défaut, "ch" vaut $0. |
index(ch1,ch2) |
Retourne la position de la sous-chaîne "ch2" dans la chaîne "ch1". |
length(ch) |
Retourne la longueur de la chaîne "ch". |
match(ch,er) |
Retourne la position dans la chaîne "ch" de la première occurrence de l’expression régulière "er". |
split(ch,tab,sep) |
Initialise le tableau "tab" avec les champs de la chaîne "ch". "sep" représente le séparateur de champ. Retourne le nombre de champs. |
sprintf(fmt,e1,...,en) |
Identique à printf, mais retourne la chaîne formatée. |
sub(er,remp,[ch]) |
Remplace dans la chaîne "ch" la première occurrence correspondant à l’expression régulière "er" par la chaîne "remp". Retourne le nombre de substitutions. Par défaut, "ch" vaut $0. |
substr(ch,pos,lg) |
Retourne la sous-chaîne de "ch" commençant à la position "pos" et de longueur "lg". |
tolower(ch) |
Retourne la valeur de la chaîne "ch" convertie en minuscules. |
toupper(ch) |
Retourne la valeur de la chaîne "ch" convertie en majuscules. |
2. Fonctions mathématiques
Fonction |
Rôle |
cos(x) |
Retourne le cosinus de x |
exp(x) |
Retourne e à la puissance x |
int(x) |
Retourne la valeur entière de x |
log(x) |
Retourne le logarithme naturel de x |
sin(x) |
Retourne le sinus de x |
sqrt(x) |
Retourne la racine carrée... |
Fonctions utilisateur
Syntaxe
Une fonction peut recevoir de 0 à n arguments et retourner une valeur explicite. Les fonctions peuvent être définies au-dessus ou au-dessous de leur appel.
Définition d’une fonction :
function nom_fonction (param1, param2 ... paramn) {
return valeur
}
Les paramètres (param1, param2 ... paramn) sont des variables locales. Toute autre variable définie dans la fonction est globale.
Appel d’une fonction :
valeur_retournee=nom_fonction(val1, val2, ..., valn)
Il ne doit pas y avoir d’espace entre le nom de la fonction et la parenthèse ouvrante.
Exemple
Le script region2.awk réaffiche le fichier tel3.txt en ajoutant un champ avant le téléphone ; ajout de la valeur " RP " pour les clients localisés en région parisienne et " REGION " pour les clients localisés en province.
La fonction getRegion() reçoit en argument le code postal et retourne la chaine représentant la région.
$ nl region2.awk
1 # fonction qui retourne la valeur de la région
2 function getRegion (cp) {
3 # Region parisienne
4 if ( cp ~ /^7[578]|9[1-5])/ ) {
5 return "RP"
6 }
7 return "REGION"
8 }
9 # Section BEGIN
10 BEGIN {
11 FS="|"
12 }
13 # Section...
Inclusions de fichiers
gawk >= 4 |
Le langage gawk offre, à partir de sa version 4, la possibilité d’inclure dans le code source le contenu d’autres fichiers, grâce à la directive @include. La variable d’environnement AWKPATH pourra être initialisée avec le nom des répertoires où gawk doit rechercher les fichiers à inclure (même principe que la variable PATH Unix). Si AWKPATH est initialisée, elle doit également contenir l’emplacement du script principal.
Exemple d’inclusion
Le fichier à inclure :
$ nl affiche.inc.gawk
1 function affiche(message) { print message }
Le programme principal :
$ nl include.gawk
1 @include "affiche.inc.gawk"
2 BEGIN { affiche("Hello") }
$ gawk -f include.gawk
Hello
$
Exemple d’utilisation de AWKPATH
Le programme principal :
/home/christie/awk/AWKPATH/include.gawk
inclut :
/home/christie/awk/AWKPATH/includes/affiche.inc.gawk
Définition de la variable AWKPATH qui doit contenir l’emplacement du programme principal et l’emplacement des fichiers à inclure :
$ AWKPATH=/home/christie/awk/AWKPATH:/home/christie/awk/AWKPATH/includes
$ export AWKPATH
$ gawk -f include.gawk
Hello
$
La variable et son exportation peuvent être placés dans le fichier .bash_profile / .profile pour un paramétrage permanent.
Exercices
Les fichiers fournis pour les exercices sont disponibles dans le répertoire dédié au chapitre sous l’arborescence Exercices/fichiers.
1. awk en ligne de commande
a. Exercice 1 : awk et autres filtres
Commandes filtres utiles : awk, grep (cf. chapitre Les commandes filtres), sed (cf. chapitre La commande sed). Autre commande utile : file.
Faire afficher les noms des fichiers texte du répertoire /etc.
Exemple de résultat
adjtime
aliases
asound.conf
auto.master
auto.misc
. . .
b. Exercice 2 : critères de sélection
1. |
Dans votre répertoire courant, afficher les caractéristiques des fichiers dont le nom commence par un point (uniquement ceux-là). |
Exemple de résultat
drwxr-xr-x. 24 christie christie 4096 3 févr. 12:26 .
drwxr-xr-x. 11 root root 4096 27 janv. 14:06 ..
-rw-------. 1 christie ociensa 14752 22 janv. 12:40 .bash_history
2. |
Dans votre répertoire courant, afficher les noms de fichiers commençant par un point, sauf "." et "..". |
Exemple de résultat
.bash_history
.bash_logout
.bash_profile
.bashrc
c. Exercice 3 : critères de sélection, affichage de champs, sections BEGIN et END
À partir du fichier php.ini fourni :
1. |
Faire afficher les lignes qui ne commencent pas par ";" et qui se terminent par On ou Off. |
Exemple de résultat
engine = On
short_open_tag = Off
asp_tags = Off
zlib.output_compression = Off
implicit_flush = Off
2. |
Améliorer l’affichage. |
Exemple de résultat
engine On
short_open_tag Off
asp_tags ...