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
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é.
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.
Voici une liste des commandes les plus utilisées dans les scripts sed :
$ 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 :
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.
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é.
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 :
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.
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 :
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
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 \n. Par exemple, afficher une ligne de données sur deux peut être réalisé de la manière suivante :
$ sed 'N;s/^.*\n//'
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.
Quelques commandes restent à évoquer :
$ printf '\x12\n' | sed -n l \022$
L'emploi des dernières commandes indiquées ici peut être fastidieux lors de l'écriture de scripts sed en ligne de commande, comme cet article l'a proposé jusqu'à présent. Aussi, des méthodes plus pratiques de lancement de sed vont-elles être indiquées ici.
Tout d'abord, le script peut être composé de plusieurs parties fournies séparément à sed, ainsi, les deux commandes
$ sed 's/foo/bar/; s/titi/toto/' $ sed -e s/foo/bar -e s/titi/toto/
sont-elles équivalentes. Une troisième manière de procéder est de stocker certaines parties du script dans des fichiers, dont les chemins sont donnés en argument de l'option -f de sed. Ainsi, cette troisième manière d'écrire les deux commandes précédentes est :
$ echo s/foo/bar/ > /tmp/foobar $ sed -f /tmp/foobar -e s/titi/toto/
Un point important est que, lorsque l'une des options -e et -f est donnée, sed considère tout argument n'étant pas une option comme un nom de fichier dans lequel des données devront être lues (ces fichiers sont traités dans leur ordre d'apparition sur la ligne de commande).
Dans le cas contraire, le premier de ces arguments est considéré comme le script à exécuter.
Enfin, il n'est pas nécessaire d'invoquer directement sed pour exécuter un script, un fichier peut être écrit et contenir une fois pour toutes la ligne de commande de sed. Il est pour cela nécessaire que la première ligne du fichier soit la suivante :
#!/bin/sed -f
Naturellement, /bin/sed doit être remplacé dans cette ligne par le chemin d'accès à votre interpréteur sed, si celui-ci n'est pas dans /bin.
Quelques points sont à prendre en compte lorsque des scripts sed sont prévus pour être interprétés par d'autres versions de l'utilitaire que celle du programmeur :