Avertissement :
La version originale de cet article a été publiée sur IBM developerWorks
et est la propriété de Westtech Information Services. Ce document est une
traduction de la mise à jour de la version originale de l'article
réalisée par l'équipe de documentation Gentoo et contient quelques
améliorations proposées par l'équipe de documentation de Gentoo Linux.
Ce document n'est pas activement maintenu.
|
Sed par l'exemple, 2e partie
1.
Comment tirer plus de profit de l'éditeur de texte UNIX ?
Substitution !
Regardons l'une des commandes les plus pratiques de Sed, la commande de
substitution. En l'utilisant, nous pouvons remplacer une chaîne de caractères
ou une expression régulière par une autre chaîne de caractères. Voici un
exemple de l'utilisation la plus fondamentale de cette commande :
Exemple de code 1.1 : L'utilisation la plus fondamentale de la commande de substitution |
$ sed -e 's/foo/bar/' myfile.txt
|
Cette commande va afficher le contenu du fichier myfile.txt à
l'écran avec la première occurrence de « foo » (si elle existe)
rencontrée sur chaque ligne, remplacée par la chaîne de caractères
« bar ». Notez-bien que j'ai dit la première occurrence de chaque
ligne, bien que ce ne soit pas normalement ce que vous voulez. Normalement,
quand je fais le remplacement d'une chaîne de caractères, je veux l'exécuter
globalement. C'est à dire que je veux remplacer toutes les occurrences sur
chaque ligne comme suit :
Exemple de code 1.2 : Remplacer toutes les occurences sur chaque ligne |
$ sed -e 's/foo/bar/g' myfile.txt
|
L'option supplémentaire « g » après le dernier slash « / »
indique à Sed de réaliser un remplacement global.
Voici quelques autres petites choses que vous devriez savoir au sujet de la
commande de substitution s///. Tout d'abord, c'est une commande et
seulement une commande : il n'y a pas de numéro de ligne spécifié dans les
exemples précédents. Cela veut dire que la commande s/// peut aussi être
utilisée avec des numéros de ligne pour préciser à quelles lignes elle doit
s'appliquer, comme suit :
Exemple de code 1.3 : Préciser les lignes auxquelles la commande doit s'appliquer |
$ sed -e '1,10s/enchantment/entrapment/g' myfile2.txt
|
L'exemple précédent entraîne le remplacement de toutes les occurrences du mot
« enchantment » par le mot « entrapment » mais seulement de
la première à la dixième ligne incluse.
Exemple de code 1.4 : Spécifier plus d'options |
$ sed -e '/^$/,/^END/s/hills/mountains/g' myfile3.txt
|
Cette exemple remplace « hills » par « mountains » mais
seulement sur les blocs de texte commençant par une ligne vide et se terminant
par une ligne commençant par les trois caractères « END ».
Une autre chose de bien avec la commande s///, c'est que l'on peut
remplacer le séparateur « / » par autre chose. Si nous
réalisons un remplacement de chaîne de caractères et que l'expression régulière
ou la chaîne de caractères de remplacement contient beaucoup de barres obliques
(slashs), nous pouvons changer le séparateur en utilisant un caractère
différent après le « s ». Par exemple, cela remplacera toutes les
occurrences de /usr/local par /usr :
Exemple de code 1.5 : Remplacer toutes les occurences d'une chaîne de caractères par une autre |
$ sed -e 's:/usr/local:/usr:g' mylist.txt
|
Note :
Dans cet exemple, nous utilisons le caractère « : » comme séparateur.
Si vous devez quand même utiliser le caractère de séparation dans l'expression
régulière, ajoutez une barre oblique inverse (backslash « \ ») devant
lui.
|
Avec des expressions régulières
Jusqu'à présent, nous avons seulement réalisé une simple substitution d'une
chaîne de caractères. Bien que ce soit pratique, nous pouvons aussi rechercher
une expression régulière. Par exemple, la commande suivante de Sed recherche
une phrase commençant par « < », se terminant par
« > » et contenant un nombre quelconque de caractères entre
« < » et « > ». Cette phrase va être supprimée
(remplacée par une chaîne de caractères vide) :
Exemple de code 1.6 : Supprimer une phrase spécifique |
$ sed -e 's/<.*>//g' myfile.html
|
C'est un bon premier essai de script Sed qui retire des balises HTML à partir
d'un fichier mais il ne marche pas bien à cause d'un caprice de l'expression
régulière. La raison ? Quand Sed recherche une expression régulière sur
une ligne, il recherche la correspondance la plus longue sur la ligne. Ceci
n'est pas une correction à mon article précédent sur Sed car nous utilisions
les commandes d et p qui supprimaient de toute façon la ligne
entière. Mais quand nous utilisons la commande s///, cela est très
différent parce que la partie entière que l'expression régulière trouve est
remplacée par la chaîne de caractères de remplacement ou, comme dans le cas
précédent, est supprimée. Cela signifie que l'exemple précédent transforme la
ligne suivante :
Exemple de code 1.7 : Exemple de code HTML |
<b>C'est</b> ce que <b>nous</b> voulons.
|
En :
Exemple de code 1.8 : Effet non désiré |
voulons.
|
Au lieu de ça, voici ce que nous souhaitions :
Exemple de code 1.9 : Effet désiré |
C'est ce que nous voulons.
|
Heureusement, il y a un moyen facile de corriger ça. Au lieu de taper une
expression régulière qui dit « un caractère "<" suivi par un nombre
quelconque de caractères et se terminant par un caractère ">" », nous
avons juste à taper une expression régulière qui dit « un caractère "<"
suivi par un nombre quelconque de caractères n'étant pas ">" et se terminant
par un caractère ">" ». Cela a pour effet de rechercher la plus petite
correspondance plutôt que la plus longue. Voici à quoi ressemble la nouvelle
commande :
Exemple de code 1.10 : |
$ sed -e 's/<[^>]*>//g' myfile.html
|
Dans l'exemple précédent, le « [^>] » correspond à un caractère
n'étant pas « > » et le « * » qui le suit complète cette
expression signifiant « zéro ou plus de zéro caractère n'étant pas
">" ». Testez cette commande sur quelques fichiers HTML puis examinez
les résultats obtenus.
Plus de caractères de correspondance
La syntaxe de l'expression régulière « [ ] » a quelques options
supplémentaires. Pour spécifier une série de caractères, vous pouvez utiliser
un « - » tant qu'il n'est pas en première ou dernière position, comme
ceci :
Exemple de code 1.11 : Spécifier une série de caractères |
'[a-x]*'
|
Cela recherche zéro caractère ou plus, tant que ceux-ci sont « a »,
« b », « c »... « v », « w » et
« x ». De plus, la classe de caractères « [:space:] » est
disponible pour rechercher une espace. Voici une liste assez complète des
classes de caractères disponibles :
| Classe de caractères |
Description |
| [:alnum:] |
Alphanumérique [a-z A-Z 0-9] |
| [:alpha:] |
Alphabétique [a-z A-Z] |
| [:blank:] |
Espace ou tabulation |
| [:cntrl:] |
Tout caractère de contrôle |
| [:digit:] |
Chiffre [0-9] |
| [:graph:] |
Tout caractère visible (sauf le caractère espace) |
| [:lower:] |
Minuscule [a-z] |
| [:print:] |
Un caractère qui n'est pas un caractère de contrôle. |
| [:punct:] |
Caractère de ponctuation |
| [:space:] |
Espace |
| [:upper:] |
Majuscule [A-Z] |
| [:xdigit:] |
Caractère hexadécimal [0-9 a-f A-F] |
Il est avantageux d'utiliser des classes de caractères lorsque cela est
possible car ils s'adaptent mieux dans les environnements non-anglophones (par
exemple, en incluant les caractères accentués lorsque cela est nécessaire).
Commandes avancées pour la substitution de chaînes de caractères
Nous avons vu comment réaliser des substitutions simples et même relativement
complexes mais Sed peut faire bien plus. Nous pouvons réellement nous référer à
des parties ou à l'expression régulière entière trouvée et utiliser ces parties
pour construire la chaîne de caractères de remplacement. Par exemple, disons
que vous répondez à un message. L'exemple suivant va préfixer chaque ligne avec
la phrase « ralph a dit » :
Exemple de code 1.12 : Préfixer chaque ligne avec une chaîne de caractères |
$ sed -e 's/.*/ralph a dit : &/' origmsg.txt
|
Le résultat affiché ressemblera à quelque chose comme ça :
Exemple de code 1.13 : Résultat affiché par la commande |
ralph a dit : Hiya Jim,
ralph a dit :
ralph a dit : I sure like this sed stuff!
ralph a dit :
|
Dans cet exemple, nous utilisons le caractère « & » dans la
chaîne de caractères de remplacement, ce qui indique à Sed d'insérer
l'expression régulière entière rencontrée. Donc, tout ce qui correspond à
« .* » (le groupe le plus large composé de zéro ou plus de caractères
sur la ligne, autrement dit la ligne entière) peu être inséré où l'on veut dans
la chaîne de caractères de remplacement, même plusieurs fois. C'est génial mais
Sed est encore plus puissant.
Ces magnifiques parenthèses backslashées
Encore mieux que « & », la commande s/// nous permet de
définir des zones dans notre expression régulière et nous pourrons nous référer
à ces zones spécifiques dans notre chaîne de remplacement. Par exemple, disons
que nous avons un fichier qui contient le texte suivant :
Exemple de code 1.14 : Exemple de texte |
foo bar oni
eeny meeny miny
larry curly moe
jimmy the weasel
|
Maintenant, disons que nous voulons écrire un script Sed qui remplace
« eeny meeny miny » par « Victor eeny-meeny Von miny »,
etc. Pour faire ça, tout d'abord nous devons écrire une expression régulière
qui doit correspondre aux trois chaînes de caractères séparées par des
espaces :
Exemple de code 1.15 : Expression régulière correspondante |
'.* .* .*'
|
Voilà. Maintenant, nous allons définir les zones en insérant des parenthèses
backslashées autour de chaque zone qui nous intéresse :
Exemple de code 1.16 : Définir des zones |
'\(.*\) \(.*\) \(.*\)'
|
Cette expression régulière travaille de la même façon que la première, exceptée
qu'elle définit trois zones logiques auxquelles nous pouvons nous référer dans
notre chaîne de caractères de remplacement. Voici le script final :
Exemple de code 1.17 : Script final |
$ sed -e 's/\(.*\) \(.*\) \(.*\)/Victor \1-\2 Von \3/' myfile.txt
|
Comme vous pouvez le voir, nous nous référons à chaque zone délimitée par des
parenthèses en tapant « \x » où x est le numéro de la région,
démarrant à un. Le résultat affiché est le suivant :
Exemple de code 1.18 : Résultat affiché par la commande précédente |
Victor foo-bar Von oni
Victor eeny-meeny Von miny
Victor larry-curly Von moe
Victor jimmy-the Von weasel
|
Au fur et à mesure que vous vous familiariserez avec Sed, vous serez capable de
réaliser des traitements de textes assez puissants avec un minimum d'efforts.
Vous pouvez réfléchir à la façon dont vous auriez traité ce problème en
utilisant votre langage de script favori : pouvez-vous facilement trouver
une solution tenant sur une seule ligne ?
Utiliser plusieurs commandes
Comme nous commençons à créer des scripts Sed plus complexes, nous avons besoin
d'être capables d'entrer plus d'une commande. Il y a plusieurs manières de
procéder. Tout d'abord, nous pouvons utiliser des points-virgules entre les
commandes. Par exemple, cette série de commandes utilise la commande
« = » qui indique à Sed de n'afficher que le numéro de ligne puis la
commande « p » qui indique à Sed d'afficher la ligne (puisque nous
sommes dans le mode « -n » :
Exemple de code 1.19 : Première méthode, les points-virgules |
$ sed -n -e '=;p' myfile.txt
|
Même si deux commandes ou plus sont spécifiées, chaque commande est appliquée
(dans l'ordre) à chaque ligne du fichier. Dans l'exemple précédent, tout
d'abord la commande « = » est appliqué à la ligne 1 puis la commande
p est appliquée. Puis, Sed traite la ligne 2 et répète le processus.
Bien que l'utilisation du point-virgule soit pratique, il y a des situations où
cela ne fonctionnera pas. Une autre solution est d'employer deux fois l'option
« -e » pour spécifier séparément les deux commandes :
Exemple de code 1.20 : Deuxième méthode, plusieurs « -e » |
$ sed -n -e '=' -e 'p' myfile.txt
|
Cependant, quand nous verrons les commandes plus complexes d'ajout et
d'insertion, même plusieurs options « -e » ne nous aideront pas. Pour
des scripts complexes de plusieurs lignes, la meilleure façon est de saisir vos
commandes dans un fichier séparé. Puis référencez ce fichier de script avec
l'option « -f » :
Exemple de code 1.21 : Troisième méthode, un fichier externe contenant les commandes |
$ sed -n -f mycommands.sed myfile.txt
|
Cette méthode, bien que discutablement moins pratique, fonctionnera toujours.
Utiliser plusieurs commandes sur un groupe de lignes
Parfois, vous pourrez vouloir spécifier plusieurs commandes qui s'appliqueront
à une simple ligne. Ceci devient particulièrement pratique quand vous réalisez
beaucoup de substituions s/// pour transformer des mots ou la syntaxe
dans le fichier source. Pour réaliser plusieurs commandes par ligne, entrez vos
commandes Sed dans un fichier et utilisez les caractères « { } »
pour grouper les commandes comme suit :
Exemple de code 1.22 : Réaliser plusieurs commandes par ligne |
1,20{
s/[Ll]inux/GNU\/Linux/g
s/samba/Samba/g
s/posix/POSIX/g
}
|
L'exemple précédent applique trois commandes de substitutions de la ligne 1 à
la ligne 20 incluse. Vous pouvez aussi utiliser une expression régulière pour
spécifier les numéros de ligne ou une combinaison des deux :
Exemple de code 1.23 : Combinaison des deux méthodes |
1,/^END/{
s/[Ll]inux/GNU\/Linux/g
s/samba/Samba/g
s/posix/POSIX/g
p
}
|
Cet exemple applique les commandes entre accolades « { } » de la
ligne 1 (première ligne) à la ligne commençant par les lettres
« END » ou la fin du fichier si « END » n'est pas trouvé
dans le fichier source.
Ajouter, insérer et modifier une ligne
Maintenons que nous savons écrire des scripts Sed dans des fichiers séparés,
nous pouvons profiter des commandes permettant d'ajouter, insérer et modifier
les lignes. Ces commandes insèrent une ligne après la ligne courante, insèrent
une ligne avant la ligne courante ou remplacent la ligne courante.
Exemple de code 1.24 : Utiliser la commande d'insertion de ligne |
i\
Cette ligne sera insérée avant chaque ligne
|
Si vous ne précisez pas les numéros de ligne auxquelles doit s'appliquer ce
script, il sera appliqué à chaque ligne et produira l'affichage de quelque
chose ressemblant à ça :
Exemple de code 1.25 : Résultat affiché par la commande précédente |
Cette ligne sera insérée avant chaque ligne
ligne 1
Cette ligne sera insérée avant chaque ligne
ligne 2
Cette ligne sera insérée avant chaque ligne
ligne 3
Cette ligne sera insérée avant chaque ligne
ligne 4
|
Si vous souhaitez insérer plusieurs lignes avant la ligne actuelle, vous pouvez
les ajouter en les terminant par un backslash « \ » comme ceci :
Exemple de code 1.26 : Insérer plusieurs lignes avant la ligne actuelle |
i\
insère cette ligne\
et celle-ci\
et celle-ci\
et, euh, celle-ci aussi.
|
La commande d'ajout fonctionne de la même façon mais elle insère une ou
plusieurs lignes après la ligne courante. Elle est utilisée comme suit :
Exemple de code 1.27 : Ajouter des lignes après la ligne courante |
a\
Ajoute cette ligne après chaque ligne. Merci ! :)
|
D'autre part, la commande de modification de ligne remplace la ligne courante
et est utilisée comme suit :
Parce que les commandes d'ajout, d'insertion et de modification de ligne ont
besoin d'être saisies sur plusieurs lignes, vous devriez les taper dans un
fichier texte et indiquer ce fichier texte contenant le script en utilisant
l'option « -f ». Utiliser les autres méthodes pour passer ces
commandes à Sed entraînera des problèmes.
La prochaine fois
La prochaine fois, dans le dernier article de cette série sur Sed, je vous
montrerai beaucoup d'exemples excellents utilisant Sed dans le monde réel pour
beaucoup de types de tâches différentes. Non seulement je vous expliquerai ce
que les scripts font mais aussi pourquoi ils font ce qu'ils font. Une fois que
ce sera fait, vous aurez des idées supplémentaires excellentes pour utiliser
Sed dans des projets variés.
2.
Ressources
Liens utiles
|