Article pour l'Echo de Linux (Novembre 1996)
Eric Dumas (dumas@linux.eu.org)Sed et Awk sont deux outils que l'on rencontre sur la plupart des plateformes Unix. Si cet article présente ces deux outils en même temps, c'est parce qu'ils sont assez souvent utilisés ensemble. En plus, ils utilisent la même syntaxe au niveau des expressions régulières.
Le nom Sed provient du terme anglais Stream EDitor. Cet outil est généralement utilisé pour effectuer des modifications dans un fichier de données (comme remplacer un mot ou une expression par une autre, ...).
Awk provient de la contraction du nom de ses auteurs : Aho, Weinberger et Kernighan. Awk est considéré comme un langage de programmation, langage qui n'est pas compilé mais interprété par le programme awk.
Cet article va décrire les versions GNU de ces deux outils, c'est à dire les versions utilisées sous Linux. Toutefois, peu de choses divergent par rapport aux versions originales. Il est quand même conseillé d'utiliser ces deux versions (sed et gawk) car leurs performances sont bien meilleures.
Cette première partie a pour but d'introduire les expressions régulières. Si vous maîtrisez parfaitement cette syntaxe (qui est également utilisée avec lex, grep, etc.), vous pouvez passer au chapitre suivant.
Une expression régulière est composée bien souvent d'une valeur (variable ou littéral) et d'un opérateur. Dans le cas de sed et awk, tout caractère excepté les caractères spéciaux que nous verrons plus tard sont interprétés comme des caractères normaux. Ce qui fait le charme d'une expression régulière (ou sa complexité), ce sont ses caractères spéciaux (également appellés méta-caractères).
En voici la description :Lors d'une première lecture, cela peut éventuellement paraître un peu trouble... voici donc quelques petits exemples :
a* : rien, a, aa, aaa, aaaa, ...(nombre infini de a) a(ab)+ : aab, aabab, aababab, ... [a-d] : a,b,c,d [a-c]-[1-3] : a,b,c,1,2,3 ^a(1de)+ : une ligne qui commence par a1de ou a1de1de, ... abde$ : une ligne qui se termine par abde toto\.txt : toto.txt ab.d : abad, abbd, abcd, abdd, ... [1-31]/[1-12]/[0-2000] : une date (1ère méthode)... 11/10/1972 ([1-9] | ( [1-2] [0-9] | [ 30 31] ) ) / ( [1-9] | [ 10-12 ] ) / [0-2000] : 2nde méthode ... toujours 11/10/1972 [lL]iberté : liberté, Liberté [pP]age [12-99] : page 12, Page 12, ..., page 99, Page 99
Bien, voila en gros ce que l'on peut dire sur les expression régulières. Passons maintenant à Sed !
Sed possède comme avantage de pouvoir être utilisé en ligne de commande (ou via un fichier dans lequel on y a mis les commandes). Le principe d'une commande sed, est qu'elle contient une expression régulière désignant les données devant être modifiées, une commande et éventuellement un texte de remplacement et des options.
Le point important est le terme commande. Sed définit un certain nombre de commandes qui permettent ainsi de manipuler facilement un tampon de données. Il faut préciser qu'à la différence d'un éditeur de texte comme emacs, lorsqu'une commande est appliquée, c'est sur tout le fichier : il n'est pas possible de choisir de traiter telle ou telle partie du fichier.
La commande de substitution s permet de substituer à une expression régulière, un texte donné. Sa syntaxe est :
sed [adresse]s/expression/remplacement/options
Par exemple :
sed s/soleil/lune fichier.in > fichier.out
Cette ligne de commande remplace tous les mots soleil par le mot lune du fichier fichier.in. Le résultat est alors renvoyé dans le fichier fichier.out.
On peut affiner le travail en choisissant de n'effectuer l'opération que dans les lignes où se trouvent le mot rendez-vous :
sed /rendez\-vous/s/soleil/lune/g fichier.in > fichier.out
Nous avons ici utilisé une option particulière. En voici la liste :
Cette commande est très utile. Par exemple, supposons que je déplace le répertoire racine d'utilisateurs, de /krakatoa vers /fujiyama (oui... certains se reconnaîtrons). Dans ce cas, il suffit de faire au niveau du fichier /etc/passwd :
sed s!/krakatoa!/fujiyama!g /etc/passwd > /tmp/passwd.new
Ici, comme le caractère / est utilisé dans les expressions, j'ai utilisé comme délimiteur le caractère !. C'est une bonne alternative. Attention car avec certains interpréteurs de commandes (comme bash), le caractère ! est vu comme un historique. Dans ce cas là, il suffit d'en utiliser un autre :
sed s#/krakatoa#/fujiyama#g /etc/passwd > /tmp/passwd.newTout autre caractère que retour chariot et backslash peut être utilisé à la place du délimiteur.
sed /expressions/d
Par exemple, pour détruire toutes les lignes vide d'un fichier :sed /^$/d
sed [adresse]commande\
expression
La commande peut être :# $a\ EOFPour l'exécuter, lancer cat /etc/passwd | sed -f /tmp/demo.sed. Un autre exemple ?
/postmaster/c\ Ha que coucou
Ce code va changer une ligne dans le fichier /etc/passwd.
Avec la commande
Quelques commandes de sed peuvent être assez pratiques, bien que moins utilisées. Par exemple la commande de numérotation de lignes (=). Supposons que dans un code C, on souhaite voir à quelle ligne est utilisé la fonction strcpy (à mettre dans un fichier également) :
#n /strcpy/{ = p }
Il existe quelques commande supplémentaires dédiées à sed pour les utilisateurs averties... Consultez la bibliographie en fin de cet article pour plus de précisions.
La première fois que j'ai utilisé awk, c'était pour de l'administration système, et plus précisément pour killer des processus (très nombreux). Dans l'exemple qui va suivre en guise d'introduction, le but est de killer tous les processus xload fonctionnant sur une machine. La différence principale avec la première utilisation que j'en avais fait, c'est que c'était sous Solaris avec un démon tftpd qui se multipliait comme des petits pains...
for i in `ps -axu | grep xload | awk '{print $2 }'` do kill -9 $i done
Bien... que fait donc ce petit exemple ? Il récupère tous les processus xload et ne conserve que la colonne contenant son pid. Ensuite, chacun d'entre eux est tué (oui, je sais, c'est très violent... mais bon, faut ce qu'il faut parfois !).
Awk est un langage très pratique car particulièrement simple comparé au langage C ou à Perl. Toutefois, il existe certaines similitudes par rapport au langage C. Les opérateurs arithmétiques et d'affectations sont les même (Awk possède en plus l'opérateur exponentiel ^) :
# Compte les lignes vides d'un fichier /^$/ { x += 1 } END { print x }
Du côté des opérateurs booléens, ce sont les même qu'en C, sauf que les opérations bit à bit n'existent pas. L'opérateur ~ est utilisé pour concorde et !~ pour ne concorde pas.
Il est à noter qu'à la différence du langage C, une instruction ne se termine pas par le sacro-saint point-virgule. Quelques fonctions tout droit rescapées du langage C sont également utilisables. Citons par exemple la fonction printf qui s'utilise exactement de la même manière, les fonctions mathématiques, trigonométriques, génération de nombres aléatoires (rand() et srand()). A noter également la présence de la fonction system() qui peut être fort utile dans certains cas.
Un programme Awk est globalement composé de trois parties :
Un exemple :
# Exemple de programme inutile qui compte le # nombre d'occurences du mot toto dans un fichier BEGIN { toto =0 } /toto/ { toto++ } END { print toto }
Un certain nombre de variables systèmes sont utilisables avec AWK :
Le système de passage de paramètres ressemble à celui qu'utilise le langage Java pour une applet. Le principe est de donner le nom de la variable suivit de sa valeur :
awk -f zorglub.awk lieu=Bordeaux
A ce moment là, la variable lieu est définie dans le script awk, comme une variable système.
Awk ne serait pas un langage sans des conditionnelles, des boucles et la possibilité de manipuler des tableaux.
Le célèbre if possède la syntaxe courante :
if (expression) if (expression) { instruction1 bloc d'instructions [else } instruction2] [else ...]
Du côté des boucles, Awk possède le while, le do... while et le for. Les boucles while, do while et for ont une syntaxe à la C :
while(expression) do instruction instruction while (exression) for (initialisation ; test ; incrémentation) instruction
Par exemple :
# Affichage de tous les nombres pairs de 0 à 99 for ( i = 0 ; i< 100 ; i++ if ((i % 2) == 0) print $i
Du côté des tableaux, aucune allocation dynamique n'est nécessaire : un tableau est directement accessible en spéciant : nom[indice].
Nous arrivons enfin à la partie la plus intéressante qui consiste à pouvoir écrire ses propres fonctions. Lors du développement d'un programme, il est possible de se passer de l'écriture de fonctions. Toutefois, la lisibilité du code en souffre rapidement.
Une fonction s'écrit très simplement :
function puissance2(nombre) { return nombre * nombre }
A noter que la notion de typage n'existe pas vraiment... De plus, la variable nombre est considérée comme étant locale à l'intérieur de la fonction.
Pour terminer ce court article sur les deux outils sed et awk, il est utile de remarquer que ces langages sont très utilisés car ils permettent d'éffectuer des opérations très puissantes d'une manière rapide et simple.
Cette courte introduction vous permet de vous familiariser avec ces deux outils. Pour aller plus loin, il peut être très utile de consulter les documents suivants :