Cet article présente l'éditeur de flux sed, l'un des utilitaires les plus répandus du monde Unix, en commençant par les commandes de base fréquemment utilisées, pour aller jusqu'aux commandes les plus évoluées et souvent méconnues.
Initiation à sed
Cet article présente l'éditeur de flux sed, l'un des utilitaires les plus répandus du monde Unix, en commençant par les commandes de base fréquemment utilisées,
pour aller jusqu'aux commandes les plus évoluées et souvent méconnues.
Sed, rendu célèbre par sa fonction de remplacement des groupes de caractères correspondant à une expression rationnelle par une chaîne de caractères constante ou non,
offre de nombreuses autres possibilités.
Nous commencerons par présenter la notion d'éditeur de flux et l'utilisation basique de sed, puis les commandes plus évoluées et moins connues de ce mini langage.
Enfin, nous évoquerons l'utilisation avancée de sed, et les problèmes de portabilité qui peuvent être rencontrés au cours de l'écriture de scripts sed.
Premiers pas avec sed
Notion d'éditeur de flux
A l'instar de l'exécution d'un programme awk, celle d'un script sed est avant tout ordonnée par les données fournies au script. Bien que ce fonctionnement puisse être
altéré par différentes commandes, le principe de base est que chaque ligne de données est successivement traitée par chacune des commandes du script une fois
et une seule.
Ainsi, s'il n'est pas impossible de réaliser des boucles en sed, ce n'est vraiment pas le but de cet utilitaire, qui ne met aucune des instructions de contrôle classiques
(while, for, etc.) à la disposition du programmeur. En outre, aucune instruction de calcul, c'est-à-dire d'addition, soustraction, masquage binaire ou décalage de bits
n'est prévue.
Ce mode de traitement, s'il n'a pas la souplesse de langages de programmation complets comme perl ou plus simples, comme awk, offre néanmoins suffisament
de possibilités pour qu'un serveur HTTP ait été écrit en sed. Au rang des autres applications exotiques réécrites en sed, on trouve également la calculatrice dc
et plusieurs bots irc.
Ces exercices de style poussent naturellement l'éditeur dans ses derniers retranchements, mais montrent sa puissance. En outre, la simplicité du fonctionnement
associé à la facilité, pour un ordinateur, d'analyser les instructions sed mènent à des performances exceptionnelles pour un langage interprété.
Premiers exemples
Une manière simple de lancer sed est de lui fournir le script de commandes à exécuter en premier argument, suivi d'une liste de fichiers où lire successivement
les données à traiter (si aucun fichier n'est spécifié, les données seront lues sur l'entrée standard). Ainsi, pour exécuter le célèbre script s/foo/bar/g, qui a pour effet
de remplacer toute occurrence de foo par bar, sur le périphérique d'entrée standard, il suffit d'utiliser la commande sed s/foo/bar/ :
$ sed s/foo/bar/g
coin
coin
foobar
barbar
Dans cet exemple, après avoir tapé la commande, la ligne coin est saisie par l'utilisateur. Dans son fonctionnement par défaut, sed affiche chaque ligne traitée
sur la sortie standard (cet affichage peut être désactivé à l'aide de l'option -n de la ligne de commande), et comme la ligne coin n'est pas affectée par la commande
s/foo/bar/g, il affiche simplement la ligne elle-même.
La seconde ligne saisie par l'utilisateur, foobar, est transformée par la commande s/foo/bar/g, sed affiche donc le résultat du traitement de la commande, barbar.
Un second exemple simple est une programmation de la commande tr à l'aide de sed, c'est-à-dire l'écriture d'un script sed qui remplace, par exemple,
tous les caractères a par b, et tous les caractères c par d :
$ sed y/ac/bd/
foobar
foobbr
Enfin, dans un script sed, plusieurs commandes peuvent être enchaînées, à condition d'être séparées par des points virgule ou des retours à la ligne :
$ sed 's/foo/bar/; y/ac/bd/'
coin
doin
foobar
bbfbbr
Sur cet exemple, le script sed s/foo/bar/; y/ac/bd/ est protégé par des simples quotes ' pour que le point virgule ne soit pas interprété par le shell.
La saisie de la ligne coin par l'utilisateur n'est pas affectée par la première commande du script, s/foo/bar, mais un d est substitué au c par la seconde commande.
Ensuite, la saisie de foobar est, comme précédemment, transformée en barbar par la première commande, puis des caractères b sont substitués aux a par la seconde
commande.
Syntaxe et principales commandes d'un script sed
Un script sed est une succession de commandes éventuellement précédées d'une adresse et éventuellement suivies de paramètres propres à la commande,
séparées par des retours à la ligne ou des points virgule.
Ces commandes sont le plus souvent constituées d'un caractère unique, et une adresse n'est autre qu'un moyen de préciser à quelle(s) ligne(s) des données
la commande s'applique (par exemple toutes les lignes contenant une certaine expression rationnelle, ou toutes les lignes entre la douzième et la quarante-deuxième).
Certaines commandes ne peuvent être précédées d'une adresse, lorsque cette précision n'aurait aucun sens.
Principales commandes sed
Voici une liste des commandes les plus utilisées dans les scripts sed :
s/expression1/expression2/indicateurs : remplace toute chaîne correspondant à l'expression rationnelle (voire man egrep pour plus de détails sur les expressions
rationnelles) expression1 par expression2. Dans expression2, peuvent apparaître les symboles 1, 2, ... 9 pour utiliser une chaîne apparaissant dans les données traitées
et correspondant à la nième sous-expression de expression1 apparaissant entre (et ). Ainsi, pour remplacer tout nombre apparaissant sur l'entrée standard par son opposé
(en supposant qu'il n'y a que des nombres entiers positifs), la commande suivante sera suffisante :
$ sed 's/([0-9]+)/-1/g'
En outre, le caractère & dans expression2, non précédé d'un antislash , est remplacé par la totalité de la chaîne de données correspondant à expression1.
Plusieurs indicateurs peuvent modifier le fonctionnement de la commande s :
l'indicateur le plus souvent rencontré est le bien connu g, qui précise que chaque occurrence d'une chaîne correspondant à expression1 doit être remplacée;
en son absence, seule le première occurrence est remplacée.
l'indicateur p entraîne l'affichage sur la sortie standard du résultat de la substitution si une substitution a eu lieu.
l'indicateur w suivi d'un caractère d'espacement et d'un nom de fichier a les mêmes effets que l'indicateur p, à la différence près que l'affichage n'a pas lieu
sur la sortie standard, mais qu'elle est écrite dans le fichier spécifié.
si un nombre n est utilisé comme indicateur, seule la nième occurrence de l'expression rationnelle est remplacée.
l'indicateur I cause l'évaluation sans respect de la casse de expression1.
Les caractères / utilisés pour séparer les expressions et les indicateurs peuvent être remplacés par n'importe quel caractère, laissé au libre choix du programmeur.
D'autre part, le caractère utilisé pour marquer la séparation peut être précédé d'un antislash dans les différentes parties de la commande pour lui retirer sa signification
spéciale.
q : cette commande cause la fin du script sed, et l'affichage de la dernière ligne traitée si l'option -n n'a pas été donnée en ligne de commande.
d : entraîne un retour immédiat au début du script sed, précédé de la lecture d'une nouvelle ligne de données.
p : affiche la ligne en cours de traitement.
n : affiche la ligne en cours de traitement si et seulement si l'option -n n'a pas été donnée en ligne de commande. Dans tous les cas, lit la ligne de données suivante,
et poursuit le traitement à l'aide de cette ligne. Si plus aucune donnée n'est disponible, met fin au programme.
y/source/destination/ : remplace, dans la ligne en cours de traitement, tout caractère apparaissant dans source par le caractère ayant la même position dans destination
(source et destination doivent être deux chaînes de même longueur).
= : affiche le numéro de la ligne en cours de traitement sur la sortie standard.
r nom de fichier : mémorise le contenu du fichier indiqué. A la prochaine lecture d'une ligne de données, ou à la fin du script, ce contenu sera affiché
sur le périphérique de sortie standard.
w nom de fichier : imprime le contenu de la ligne en cours de traitement dans le fichier spécifié. Une petite précision doit être donnée concernant les écritures de sed,
qu'il s'agisse de la commande w ou de l'indicateur w de la commande s : les fichiers ouverts en écriture par sed sont tronqués s'ils existaient au moment de l'ouverture,
et créés dans le cas contraire.
D'autre part, ils restent ouverts jusqu'à la fin du script, des écritures successives sont donc systématiquement ajoutées à la fin du fichier, et ne détruisent pas
les résultats des écritures précédentes. Enfin, une ligne commençant par un signe # est un commentaire, le reste de la ligne est ignoré.
Adresses
La plupart des commandes peuvent être précédées d'une adresse, dont le rôle est d'indiquer à quelles lignes des données la commande concernée s'applique.
Les différents formats d'adresses utilisables sont les suivants :
un nombre n précise que seule la nième ligne de données est concernée par la commande (les lignes de données sont numérotées à partir de 1).
$ correspond à la dernière ligne du dernier fichier de données.
/expression/ élimine, pour la commande qui la suit, toutes les lignes de données ne contenant aucune chaîne de caractères correspondant à l'expression rationnelle
précisée, ce mode de sélection d'adresses peut être suivi d'un I pour indiquer une correspondance non sensible à la casse.
%expression% a le même effet que le format d'adresse précédent, mais offre la possibilité d'encadrer l'expression rationnelle à l'aide de caractères différents
du slash / (n'importe quel caractère peut être utilisé en lieu et place des signes %), ce mode de sélection d'adresses peut être suivi d'un I pour indiquer une correspondance
non sensible à la casse.
adresse1, adresse2 sélectionne toutes les lignes à partir de la première sélectionnée par adresse1 et jusqu'à la première sélectionnée par adresse2, inclusivement;
cependant, la recherche de adresse2 ne commencera qu'a la ligne suivant adresse1 si adresse2 est une expression rationnelle.
adresse! élimine toutes les lignes qui auraient été sélectionnées par adresse, et sélectionne les autres.
nombre1~nombre2 sélectionne toutes les lignes dont le numéro est égal à nombre1 plus un nombre entier (éventuellement nul) de fois nombre2.
Enfin, plusieurs commandes peuvent être regroupées entre accolades, auquel cas elles partageront le même sélecteur d'adresses (l'accolade fermante doit être précédée
d'un retour à la ligne ou d'un point virgule). Ceci est utile si de nombreuses commandes doivent s'appliquer aux mêmes lignes.
Labels
Une autre caractéristique intéressante de sed, permettant par exemple de réaliser des boucles et des tests, est l'utilisation de labels. Un label peut être défini à l'aide
de la syntaxe :
:label
Deux commandes mettent une telle déclaration à profit :
la commande b label cause un saut immédiat à la position du script où label est défini (si label est omis, sed saute au début du script, affiche la ligne en cours
de traitement si l'option -n n'a pas été donnée, et lit une nouvelle ligne de données).
la commande t label cause un saut à la position du script où label est défini à condition qu'une commande s au minimum ait causé une substitution depuis la lecture
de la dernière ligne ou le dernier saut dû à une commande t.
A l'aide de labels, une ébauche de script de vérification de la validité d'une liste de noms DNS peut être très simplement écrite.
Le script suivant vérifie que les noms DNS transmis sur l'entrée standard ne commencent pas par un chiffre ou un signe -, si une telle erreur apparaît, le nom invalide
est alors affiché sur la sortie standard :
$ sed -n 's/^([-0-9])/invalide : 1/;t print;d; :print;p'
valid-hostname
1nvalid-hostname
invalide: 1nvalid-hostname
Lors de la saisie de la première ligne par l'utilisateur, comme valid-hostname ne commence ni par un chiffre, ni par un signe -, aucune substitution n'est réalisée
par la commande s, le saut au niveau de la commande t n'a donc pas lieu, et la commande d, qui a pour effet de lire une nouvelle ligne de données et de retourner
au début du script est exécutée.
Lors de la saisie de la seconde ligne, étant donné que 1nvalid-hostname commence par un chiffre, la commande s remplace ce chiffre par "invalide : 1",
c'est-à-dire par lui-même précédé de "invalide :", et la commande t cause un saut jusqu'au label print, la commande d n'est donc pas exécutée.
Vient enfin la commande p qui cause l'affichage de la ligne en cours de traitement, suivie de la lecture d'une nouvelle ligne et d'un retour au début du script.
Utilisation avancée de sed
Réordonner les données
Nous avons jusqu'à présent considéré les données ligne par ligne. Toutefois, sed les stocke en réalité dans un tampon dit pattern space. C'est dans ce tampon
que les données sont lues, puis modifiées en place par les différentes commandes.
Certaines d'entre elles permettent d'ailleurs d'étendre le tampon au-delà d'une ligne unique. En outre, s'il n'existe pas de variables à proprement parler dans le langage sed,
le pattern space peut cependant être copié vers une zone de sauvegarde dite hold space. Il peut ensuite être restauré à l'aide d'une copie en sens inverse.
Tout ce qui a été dit jusqu'à présent concernant les lignes de données concerne en réalité le pattern space. Ainsi, la commande s permet d'appliquer des expressons
rationnelles multi lignes, où le retour à la ligne est représenté par la succession de caractères
. Par exemple, afficher une ligne de données sur deux peut être réalisé
de la manière suivante :
$ sed 'N;s/^.*
//'
La commande N a pour effet d'ajouter au pattern space un retour à la ligne suivi de la prochaine ligne de données. La première ligne de données du pattern space
est ensuite remplacée par une chaîne vide à l'aide de la commande s.
Enfin, à la fin du script sed, le pattern space est affiché, puisque l'option -n n'a pas été donnée en ligne de commande, une nouvelle ligne de données est lue
est devient le nouveau pattern space, et l'exécution recommence au début du script sed, avec cette nouvelle ligne.
Commandes supplémentaires
Quelques commandes restent à évoquer :
la commande l affiche le pattern space de manière non ambiguë : les caractères non imprimables sont remplacés par des antislashs suivis de la valeur octale
du code ascii du caractère concerné, et les lignes sont terminées par des signes $ :
$ printf 'x12
' | sed -n l
|