Commandes groupées
Sed permet de grouper les commandes avec des accolades. Cela permet de faire plus de choses dans une même commande (et de se rendre compte des limites de l'uniligne, même si nous allons continuer avec

).
On peut ainsi faire des trucs comme :
sed -e '/motif_1/ {commande1; commande2} ; /motif_2/ {commande1; commande2; /sous_motif/ {commande1; commande2} }'
Je n'ai pas d'exemple intéressant à vous fournir pour l'instant, nous verrons des exemples plus loin.
Utilisation multiligne
Un des inconvénients principaux pour l'instant est l'impossibilité de travailler sur plus d'une ligne à la fois.
Pour peu que nous restions raisonnables (c'est-à-dire dans l'optique du traitement ligne par ligne), sed nous propose une commande afin de permettre de travailler sur plusieurs lignes

. Mais auparavant, un peu de théorie sur le fonctionnement interne de sed s'impose.
Théorie
On pourrait résumer la manière dont sed travaille ainsi :
- Lecture d'une ligne sur le flux d'entrée, et stockage dans l'espace de travail.
- Traitement : exécute séquentiellement les commandes reçues sur l'espace de travail.
- Envoie la ligne au flux de sortie stdout.
- Passe à la ligne suivante...
Cette notion d'espace de travail est importante. Par exemple, la regex
/$/ représente la fin de l'espace de travail, et non la fin de la ligne courante (
\n). Cela aura un impact dans nos traitements sur plusieurs lignes.
La ligne lue est stockée sans le caractère de fin de ligne, et envoyée au flux de sortie en ajoutant un caractère de fin de ligne. Ce qui fait par exemple que ce script ne fonctionnera pas :
Code : Console | $ sed 's/\n//' test.txt
Bonjour,
Ceci est un fichier de test.
Ici la ligne numéro 4.
# ceci pourrait être un commentaire
Ici la ligne numéro 7.
Au revoir |
Nous avons essayé ici de supprimer les fins de ligne, mais la manière dont fonctionne sed ne nous permet pas de procéder de la sorte.
La commande N
La solution est d'insérer une ligne supplémentaire dans l'espace de travail. Cette ligne sera ajoutée à la suite d'un caractère de fin de ligne, ce qui va nous autoriser à faire du traitement multiligne.
Il existe une commande pour cela : la commande N.
Maintenant, attention, ce n'est pas tout. Si nous faisons par exemple :
sed -e 'N; s/\n//' test.txt, on obtiendra
Code : Console | $ sed -e 'N; s/\n//' test.txt
Bonjour,
Ceci est un fichier de test.Ici la ligne numéro 4.
# ceci pourrait être un commentaire
Ici la ligne numéro 7.
Au revoir |
Une fin de ligne sur deux a été supprimée, ce qui est normal.
Rappelez-vous, sed applique le traitement puis recommence, donc il charge une ligne, charge la seconde, supprime le caractère
\n (fin de ligne) trouvé, affiche la sortie
(ce qui a pour effet de vider l'espace de travail), puis recommence...
La commande D
La commande d
(delete) dispose aussi d'un équivalent spécialisé pour le traitement multiligne : c'est la commande D, qui efface le contenu de l'espace de travail jusqu'au premier caractère
\n trouvé. S'il reste des données dans l'espace de travail, sed va relancer son traitement dessus. Sinon, une nouvelle ligne sera chargée.
Exemple d'utilisation : si l'on veut par exemple supprimer les lignes vides d'un fichier, on ne peut pas le faire comme ceci :
sed -re '/^$/ {N; s/\n//}' fichier, à cause de la particularité évoquée ci-dessus. En effet, si nous avons plusieurs lignes vides à la suite, certaines vont rester.
On pourra par contre le faire comme cela :
sed -re '/^$/ {N; D}' fichier
Labels et branchements
Une autre fonctionnalité importante et qui ajoute énormément de puissance à sed est la possibilité de branchement dans le script. Il est possible en effet de revenir en arrière dans le script, en créant des labels.
Un label s'écrit
:label.
Les : font partie du label, ce sont eux qui indiquent que le mot est un label.
Exemple concret d'utilisation
Supposons que vous ayez devant vous un fichier HTML, et que vous voulez supprimer toutes les balises qu'il contient et ne garder que le texte.
Pourquoi on ne peut pas faire sed -re 's/<[^>]*>//g' fichier.html ?
Ce script est très bien, mais le problème est que si vous avez des balises sur plusieurs lignes, votre truc ne va pas marcher...
Ce qu'il faudrait, c'est de vérifier à la fin de chaque traitement s'il ne reste pas une balise ouverte, et si c'est le cas pouvoir charger une nouvelle ligne dans l'espace de travail
et relancer le traitement.
Pour faire cela, il y a la commande b
(branch).
Voici un script qui marche même avec des balises sur plusieurs lignes :
sed -re ':start s/<[^>]*>//g; /</ {N; b start}' fichier.html