Sed - Introduction à SED - Part II
baissaoui
Messages postés
498
Date d'inscription
jeudi 2 septembre 2021
Statut
Webmaster
Dernière intervention
21 août 2024
-
21 juin 2022 à 12:58
Document initial créé par Jipicy
Partie I
Partie III
Dans la plupart des cas, ce choix par défaut est très satisfaisant, mais peut s'avérer vite un vrai casse-tête si le motif ou la chaîne de remplacement contiennent eux aussi un ou des slash(es) comme c'est souvent le cas, par exemple, pour les chemins d'une arborescence (PATH).
Vous pouvez bien évidemment protéger ce(s) slash(es) en les préfixant d'un "\" (backslash - anti-slash - barre oblique inverse), opération très fastidieuse et contraignante s'il en est, si le motif (ou la chaîne de remplacement) en comporte plusieurs et qui plus est, rendant la lisibilité du code assez difficile :
voire même impossible lorsque le motif (ou la chaîne de remplacement) est une variable devant être interprétée :
ou (expression entre double quotes)
Fort heureusement, Sed permet de remplacer le délimiteur par défaut par le caractère de son choix (#,|,!,§,etc.) à partir du moment où il ne se trouve pas dans la composition du motif (ou la chaîne de remplacement) :
Ce caractère peut tout aussi bien être une lettre quelconque si cette lettre ne fait pas partie du motif (ou la chaîne de remplacement) :
Notez que si vous utilisez le caractère "!" (point d'exclamation) comme séparateur, veillez à entourer l'expression par des quotes simples afin de prévenir toute interprétation par le shell du caractère "!" (employé normalement pour gérer l'historique des commandes).
Prenons toujours l'exemple d'un chemin comme critère de recherche. Sous cette forme, avouez que ce n'est pas trop lisible :
Alors qu'en choisissant commme caractère délimiteur "#" (dièse) par exemple, ça donnerait :
Nous pourrions écrire alors :
Le métacaractère "&" (esperluette) va nous permettre de remplacer toutes chaînes de caractères mises en correspondance par le motif (ou l'expression régulière) fourni en 1er argument.
Ici la notion de 1er argument n'est pas très explicite et peu loquace, mais elle prendra toute son importante avec les expressions régulières et lors du chapitre suivant sur les "sous-expressions".
Donc notre commande s'écrira plutôt de cette façon :
Imaginons que nous ayions à rechercher toutes chaînes numériques (1 ou plusieurs chiffres consécutifs) dans un fichier et que nous voulions faire précéder chacune de ces chaînes par "n° " (notez l'espace après le "°"). La commande ressemblerait à :
Il faut impérativement utiliser l'expression "\&" pour obtenir un "&" littéral dans une chaîne de remplacement sous peine de générer des erreurs incompréhensibles parfois.
Une sous-expression est une partie d'une expression régulière, encadrée par des parenthèses, que l'on souhaite réutiliser dans la chaîne de remplacement. Les parenthèses doivent être elles-mêmes protégées par des backslashes, à moins que l'option "-r" ait été employée.
Pour faire référence à chaque bloc mis en correspondance dans la chaîne de remplacement, on le désigne par un numéro correpondant à son ordre d'apparition dans l'expression régulière. Ce numéro doit être également protégé par un backslash. On ne peut faire référence qu'à 9 sous-expressions numérotées de \1 à \9. Ces références sont aussi appelées "références arrières".
Voici quelques exemples avec des noms de villes et leur code postal associé :
Fichier de référence :
Dans cet exemple, la sous-expression "\([0-9]*\)" est mise en correspondance avec toute chaîne exclusivement numérique et est référencée par la référence arrière "\1" :
Cette fois-ci, nous mettons en correspondance 2 sous-expressions, une exclusivement numérique et l'autre comprenant le reste de la ligne sans toutefois englober le caractère de tabulation qui sépare le code postal du nom de la ville. Nous nous servons ensuite des références arrières "\1" (code postal) et "\2" (nom de ville) pour afficher le résultat sous la forme : nom de ville > code postal
Dans ce 3ème et dernier exemple, nous ferons correspondre une sous-expression à chaque partie de la ligne, à savoir le code postal (\1), la tabulation (\2) et le nom de la ville (\3)
Une référence arrière peut faire appel à une sous-expression autant de fois que l'on veut dans la chaîne de remplacement. Reprenant le dernier exemple, en voici la démonstration avec la répétition de la tabulation à divers endroits :
Pour illustrer cet état de fait, reprenons notre fichier avec les villes et les codes postaux.
Nous allons chercher la (les) ligne(s) contenant le motif "Montpellier", les autres seront effacées (d) et nous substituerons "Montpellier" par "Béziers" :
Pour faire le tour de la question concernant les sous-expressions, références arrières et expression régulière précédente, voilà toujours selon l'exemple précédent une autre facette des possibilités offertes par Sed. Si le motif recherché est encadré par des parenthèses, il devient de ce fait une sous-expression qui peut être appelée dans la partie droite de la commande "s" :
Il suffit pour cela de faire suivre le motif (ou le numéro de ligne ou encore l'intervalle de ligne) du caractère "!" comme suit :
On rencontre souvent dans les scripts Sed, l'expression "$!" qui peut se traduire par "tant que la dernière ligne n'est pas atteinte" et qui permet d'effectuer une ou plusieurs commande(s) tant que cette condition est vraie.
Il peut être amusant de faire la comparaison entre les différentes façons d'écrire la syntaxe d'une commande pour un résultat identique.
(intervalle d'adresse autorisée)
C'est bien là leur principale fonction, cibler une ligne (ou plusieurs) et appliquer successivement une ou plusieurs commande(s) bien spécifique(s). Notons que le traitement agit sur le contenu de la mémoire tampon et non sur la ligne originale et que de ce fait, les changements apportés peuvent conditionnés les critères de sélection ultérieurs.
Dans un regroupement de commandes (dans un script), chaque commande doit débuter sur sa propre ligne, les accolades et l'ensemble des commandes doivent être sur des lignes séparées. L'accolade ouvrante doit se trouver en fin de ligne, alors que l'accolade fermante doit impérativement se trouver seule sur une ligne. Attention, aucun espace ne doit figurer après les accolades.
En effet, Sed, par défaut, utilise des quotes simples pour entourer ses expressions, or ce mécanisme en shell bloque justement l'expansion des variables ; de ce fait, l'emploi de quotes simples empêchera tout bonnement l'interprétation de la variable.
Dans un premier temps, il est tout à fait possible de remplacer ces quotes simples par des quotes doubles, ce qui, dans la plupart des cas, suffira amplement à permettre l'interprétation de la variable comme il se doit.
Mais il peut s'avérer judicieux d'employer plutôt cette syntaxe :
Dans ce cas-là, il n'est pas très aisé de comprendre pourquoi il est préférable d'utiliser un mélange de quotes simples et doubles. En fait, le mot "mélange" n'est pas le terme approprié, il aurait été plus juste d'employer le mot "exclure", car, en effet, par cette syntaxe, nous excluons la variable de l'expression, ce qui permet son interprétation par le shell, mais protège au sein de l'expression elle-même l'interprétation d'éventuels caractères propres au shell. Pour mieux comprendre cette nuance, prenons l'exemple suivant :
L'affichage par le shell de 6 lettres de l'alphabet, chacune sur une ligne et dont la lettre "A" est répétée deux fois. Après avoir affecté cette lettre (A) à une variable ($var), nous allons demander à Sed de ne pas afficher les lignes contenant cette variable, donc en employant la synatxe évoquant la négation, autrement dit le point d'exclamation (!).
Tout d'abord, écrivons l'expression avec des quotes simples :
Nous constatons que toutes les lettres sont affichées et c'est normal vu que la variable n'a pas été interprétée par le shell à cause des quotes simples.
Maintenant, entourons l'expression avec des quotes doubles :
Le shell nous renvoie un message d'erreur ! Effectivement, le "!" est un caractère réservé du shell qui sert notamment pour l'historique des commandes et qui n'a pu être interprété tel quel.
Dissocions donc la variable de l'expression propre à Sed en refermant l'expression juste avant la variable et en la rouvrant juste après à l'aide des quotes simples (notez que les quotes doubles autour de la variable sont facultatives, mais qu'il est préférable de les laisser, parce que c'est une bonne habitude à prendre et pour se prémunir d'éventuelles erreurs dues à des espaces au sein de la variable) :
Une expression régulière est un motif (pattern en anglais) qui entre en correspondance, de la gauche vers la droite, avec une chaîne de caractères.
Les expressions régulières tirent leur puissance de leurs capacités d'inclure des alternatives et des répétitions dans l'élaboration du modèle constituant le motif. Ces spécificités sont élaborées à l'aide de caractères spéciaux, qui ne sont pas interprétés au sens littéral du terme, mais d'une façon spécifique.
Voici une brève description de la syntaxe des expressions régulières utilisée dans Sed.
caractère (un caractère quelqconque)
*
\+
\?
\{i\}
\{i,j\}
\{i,\}
\(regexp\)
. un point
^
$
[liste]
Les caractères $, *, ., [, et \ ne sont pas normalement considérés comme des caractères spéciaux à l'intérieur d'une liste. Ainsi l'expression [\*] correspondra autant au caractère \ qu'au caractère *, le \ n'étant pas vu comme un caractère d'échappement servant à protéger le caractère *.
[^liste]
regexp1\|regexp2
\nombre
\n
\métacaractère
Note :
\b
\f
\n
\r
\t
\v
\o000
\dDDD
\xHH
'
\b
\B
\w
\W
\s
\S
\<
\>
\e
\l
\L
\u
\U
[:alpha:]
[:digit:]
[:lower:]
[:upper:]
[:print:]
[:punct:]
[:space:]
[:blank:]
[:graph:]
[:cntrl:]
[:xdigit:]
Voilà un mini tutoriel pour l'emploi de "sedsed".
Signification en sortie :
PATT : Affiche le contenu de l'espace de travail (mémoire principale)
HOLD : Affiche le contenu de l'espace annexe (mémoire secondaire)
COMM : La commande SED devant être exécutée
$ Délimite le contenu de PATT et HOLD
La syntaxe la plus courante reste le mode débogage simple (-d) :
Schéma : debogage_-d.png
En mode indentation :
Schéma : debogage_indent.png
Cacher l'affichage de l'espace annexe :
Pour certaines tâches dont "awk" et "perl" s'acquittent de façon plus simple et plus rapide, comme par exemple :
SED - The Stream EDitor - Part II
Ce document est une introduction à la pratique et à l'utilisation de l'éditeur de flux "SED", qui essaie de couvrir certaines fonctionnalités assez méconnues, pour ne pas dire "quasi inconnues", qui font de "SED" un outil indispensable dans la boîte à outils de tout Linuxien désireux de se rompre aux maniements et aux arcanes du traitement de fichiers via une console et un shell.Sommaire part II
- Les délimiteurs
- Le métacaractère &
- Les sous-expressions et références arrières
- La négation
- Le regroupement de commandes
- Le remplacement de variables
- Les expressions régulières
- Les différentes versions
- Debuggers
- Quand ne dois-je pas utiliser Sed ?
- Limites connues des différentes versions
- Les références
- SED - The Stream EDitor - Part III
Partie I
Partie III
Les délimiteurs
Délimiteur de commande
Conventionnellement, Sed utilise comme caractère délimiteur pour son mécanisme de substitution le caractère "/" (slash - barre oblique) :sed 's/motif/remplacement/' fichier
Dans la plupart des cas, ce choix par défaut est très satisfaisant, mais peut s'avérer vite un vrai casse-tête si le motif ou la chaîne de remplacement contiennent eux aussi un ou des slash(es) comme c'est souvent le cas, par exemple, pour les chemins d'une arborescence (PATH).
Vous pouvez bien évidemment protéger ce(s) slash(es) en les préfixant d'un "\" (backslash - anti-slash - barre oblique inverse), opération très fastidieuse et contraignante s'il en est, si le motif (ou la chaîne de remplacement) en comporte plusieurs et qui plus est, rendant la lisibilité du code assez difficile :
sed 's//home/jp/Docs/CCM/SED//mnt/serveur/docs/' fichier
voire même impossible lorsque le motif (ou la chaîne de remplacement) est une variable devant être interprétée :
var="/home/jp/Documents/CCM/SED/"
sed 's/'$var'//mnt/serveur/docs/' fichier
ou (expression entre double quotes)
sed "s/$var//mnt/serveur/docs/" fichier
Fort heureusement, Sed permet de remplacer le délimiteur par défaut par le caractère de son choix (#,|,!,§,etc.) à partir du moment où il ne se trouve pas dans la composition du motif (ou la chaîne de remplacement) :
sed 's#/home/jp/Docs/CCM/SED#/mnt/serveur/docs#' fichier
Ce caractère peut tout aussi bien être une lettre quelconque si cette lettre ne fait pas partie du motif (ou la chaîne de remplacement) :
echo "bonjour" | sed 'sZbZBZ'
Notez que si vous utilisez le caractère "!" (point d'exclamation) comme séparateur, veillez à entourer l'expression par des quotes simples afin de prévenir toute interprétation par le shell du caractère "!" (employé normalement pour gérer l'historique des commandes).
Délimiteur de motif
Comme nous l'avons vu, Sed utilise des motifs, encadrés par le caractère "/" (slash), pour rechercher (mettre en correspondance) certaines lignes d'un document. Le slash utilisé par convention peut cependant être modifié lui aussi au profit d'un autre caractère tout simplement en faisant précéder la 1ère occurrence de ce caractère par un "\" (backslash).Prenons toujours l'exemple d'un chemin comme critère de recherche. Sous cette forme, avouez que ce n'est pas trop lisible :
sed -n '//home/jp/Docs/CCM/SED/p' fichier
Alors qu'en choisissant commme caractère délimiteur "#" (dièse) par exemple, ça donnerait :
sed -n '#/home/jp/Docs/CCM/SED#p' fichier
Le métacaractère &
Bien souvent le mécanisme de substitution se limite à chercher un motif afin de le substituer à lui-même en lui ajoutant une succincte partie comme, par exemple, rechercher la chaîne "Sed the Stream EDitor" dans un document et vouloir lui ajouter systèmatiquement "Sed the Stream EDitor (Éditeur de flux)".Nous pourrions écrire alors :
sed 's/Sed the Stream EDitor/Sed the Stream EDitor (Éditeur de flux)/g' fichier
Le métacaractère "&" (esperluette) va nous permettre de remplacer toutes chaînes de caractères mises en correspondance par le motif (ou l'expression régulière) fourni en 1er argument.
Ici la notion de 1er argument n'est pas très explicite et peu loquace, mais elle prendra toute son importante avec les expressions régulières et lors du chapitre suivant sur les "sous-expressions".
Donc notre commande s'écrira plutôt de cette façon :
sed 's/Sed the Stream EDitor/& (Éditeur de flux)/g' fichier
Imaginons que nous ayions à rechercher toutes chaînes numériques (1 ou plusieurs chiffres consécutifs) dans un fichier et que nous voulions faire précéder chacune de ces chaînes par "n° " (notez l'espace après le "°"). La commande ressemblerait à :
sed 's/[0-9][0-9]*/n° &/g' fichier
Il faut impérativement utiliser l'expression "\&" pour obtenir un "&" littéral dans une chaîne de remplacement sous peine de générer des erreurs incompréhensibles parfois.
Les sous-expressions et références arrières
Les sous-expressions
\(...\)Une sous-expression est une partie d'une expression régulière, encadrée par des parenthèses, que l'on souhaite réutiliser dans la chaîne de remplacement. Les parenthèses doivent être elles-mêmes protégées par des backslashes, à moins que l'option "-r" ait été employée.
Les références arrières
\1 \2 \5Pour faire référence à chaque bloc mis en correspondance dans la chaîne de remplacement, on le désigne par un numéro correpondant à son ordre d'apparition dans l'expression régulière. Ce numéro doit être également protégé par un backslash. On ne peut faire référence qu'à 9 sous-expressions numérotées de \1 à \9. Ces références sont aussi appelées "références arrières".
Voici quelques exemples avec des noms de villes et leur code postal associé :
Fichier de référence :
$ cat plop
31000 Toulouse
34000 Montpellier
66000 Perpignan
Dans cet exemple, la sous-expression "\([0-9]*\)" est mise en correspondance avec toute chaîne exclusivement numérique et est référencée par la référence arrière "\1" :
$ sed 's/([0-9]*).*/\\1/' plop
31000
34000
66000
Cette fois-ci, nous mettons en correspondance 2 sous-expressions, une exclusivement numérique et l'autre comprenant le reste de la ligne sans toutefois englober le caractère de tabulation qui sépare le code postal du nom de la ville. Nous nous servons ensuite des références arrières "\1" (code postal) et "\2" (nom de ville) pour afficher le résultat sous la forme : nom de ville > code postal
$ sed 's/([0-9]*)t(.*)/2 > 1/' plop
Toulouse > 31000
Montpellier > 34000
Perpignan > 66000
Dans ce 3ème et dernier exemple, nous ferons correspondre une sous-expression à chaque partie de la ligne, à savoir le code postal (\1), la tabulation (\2) et le nom de la ville (\3)
$ sed 's/([0-9]*)(t)(.*)/321/' plop
Toulouse 31000
Montpellier 34000
Perpignan 66000
Une référence arrière peut faire appel à une sous-expression autant de fois que l'on veut dans la chaîne de remplacement. Reprenant le dernier exemple, en voici la démonstration avec la répétition de la tabulation à divers endroits :
$ sed 's/([0-9]*)(t)(.*)/23221/' plop
Toulouse 31000
Montpellier 34000
Perpignan 66000
Expression régulière précédente
Lors de la mise en corrrespondance d'une chaîne avec une expression régulière, Sed met dans sa mémoire tampon ladite chaîne et de ce fait, il est alors possible de se référer à cette chaîne dans la 1ère partie de la commande de substitution "s" (LHS) sans en faire mention littéralement. Autrement dit, une commande du genre 's//chaîne_de_remplacement/' substituera la dernière expression régulière mise en correspondance par Sed par la chaîne_de_remplacement.Pour illustrer cet état de fait, reprenons notre fichier avec les villes et les codes postaux.
Nous allons chercher la (les) ligne(s) contenant le motif "Montpellier", les autres seront effacées (d) et nous substituerons "Montpellier" par "Béziers" :
$ cat plop
31000 Toulouse
34000 Montpellier
64000 Perpignan
$ sed '/Montpellier/!d; s//Béziers/' plop
34000 Béziers
Pour faire le tour de la question concernant les sous-expressions, références arrières et expression régulière précédente, voilà toujours selon l'exemple précédent une autre facette des possibilités offertes par Sed. Si le motif recherché est encadré par des parenthèses, il devient de ce fait une sous-expression qui peut être appelée dans la partie droite de la commande "s" :
$ sed '/(Montpellier)/!d;s//Béziers-1/' plop
34000 Béziers-Montpellier
$ sed '/(Mon)tpellier/!d;s//Béziers-1blanc/' plop
34000 Béziers-Monblanc
La négation
Il peut s'avérer parfois utile d'exclure une ligne matchant un motif (ou intervalle de ligne) avant d'effectuer un traitement. Pour ce faire, Sed a recours au caractère "!" (point d'exclamation), qui, comme dans la plupart des outils issus du monde Unix, évoque la "négation", exactement comme le fait la commande "grep -v".Il suffit pour cela de faire suivre le motif (ou le numéro de ligne ou encore l'intervalle de ligne) du caractère "!" comme suit :
sed -n '3 !p' fich.txt
sed -n '3,8 !p' fich.txt
sed -n '/indice/! p' fich3.txt
On rencontre souvent dans les scripts Sed, l'expression "$!" qui peut se traduire par "tant que la dernière ligne n'est pas atteinte" et qui permet d'effectuer une ou plusieurs commande(s) tant que cette condition est vraie.
Il peut être amusant de faire la comparaison entre les différentes façons d'écrire la syntaxe d'une commande pour un résultat identique.
echo -e 'anbnncndnennfng' | sed '/./! d'
echo -e 'anbnncndnennfng' | sed '/^$/ d'
echo -e 'anbnncndnennfng' | sed -n '/^$/! p'
echo -e 'anbnncndnennfng' | sed -n '/./ p'
Le regroupement de commandes
/adresse/{...}(intervalle d'adresse autorisée)
- Les accolades permettent de regrouper certaines commandes à effectuer sur une adresse ou une plage d'adresses. On peut trouver au sein de ces commandes regroupées d'autres sélections d'adresses ainsi que d'autres commandes regroupées, entre accolades elles aussi.
C'est bien là leur principale fonction, cibler une ligne (ou plusieurs) et appliquer successivement une ou plusieurs commande(s) bien spécifique(s). Notons que le traitement agit sur le contenu de la mémoire tampon et non sur la ligne originale et que de ce fait, les changements apportés peuvent conditionnés les critères de sélection ultérieurs.
sed '
/a/{ # seule la ligne contenant "a"
s/a/c/g # remplacer tous les "a" par "c"
/c/{ # seule la ligne contenant "c" de la ligne matchée
s/c/A/g # remplacer tous les "c" par "A"
}
}
' < <(echo -e "aaanbbbncccnddd")
AAA
bbb
ccc
ddd
$ echo -e "aaanbbbncccnddd" | sed '/a/{s/a/c/g;/c/{s/c/A/g}}'
AAA
bbb
ccc
ddd
Dans un regroupement de commandes (dans un script), chaque commande doit débuter sur sa propre ligne, les accolades et l'ensemble des commandes doivent être sur des lignes séparées. L'accolade ouvrante doit se trouver en fin de ligne, alors que l'accolade fermante doit impérativement se trouver seule sur une ligne. Attention, aucun espace ne doit figurer après les accolades.
Le remplacement de variables
Il peut arriver qu'à l'intérieur d'un script Sed on ait besoin de passer une variable comme paramètre, que ce soit comme motif ou encore dans l'une ou l'autre des parties (LHS ou RHS) lors d'une substitution. Comme je vous en ai parlé précédemment, il faudra faire très attention aux caractères présents dans la variable afin d'adapter les quotes à utiliser.En effet, Sed, par défaut, utilise des quotes simples pour entourer ses expressions, or ce mécanisme en shell bloque justement l'expansion des variables ; de ce fait, l'emploi de quotes simples empêchera tout bonnement l'interprétation de la variable.
var=A; echo 'azerty' | sed 's/a/$var/'
$varzerty
Dans un premier temps, il est tout à fait possible de remplacer ces quotes simples par des quotes doubles, ce qui, dans la plupart des cas, suffira amplement à permettre l'interprétation de la variable comme il se doit.
var=A; echo 'azerty' | sed "s/a/$var/"
Azerty
Mais il peut s'avérer judicieux d'employer plutôt cette syntaxe :
var=A; echo 'azerty' | sed 's/a/'"$var"'/'
Azerty
Dans ce cas-là, il n'est pas très aisé de comprendre pourquoi il est préférable d'utiliser un mélange de quotes simples et doubles. En fait, le mot "mélange" n'est pas le terme approprié, il aurait été plus juste d'employer le mot "exclure", car, en effet, par cette syntaxe, nous excluons la variable de l'expression, ce qui permet son interprétation par le shell, mais protège au sein de l'expression elle-même l'interprétation d'éventuels caractères propres au shell. Pour mieux comprendre cette nuance, prenons l'exemple suivant :
L'affichage par le shell de 6 lettres de l'alphabet, chacune sur une ligne et dont la lettre "A" est répétée deux fois. Après avoir affecté cette lettre (A) à une variable ($var), nous allons demander à Sed de ne pas afficher les lignes contenant cette variable, donc en employant la synatxe évoquant la négation, autrement dit le point d'exclamation (!).
Tout d'abord, écrivons l'expression avec des quotes simples :
var=A; echo -e "AnBnCnAnDnE" | sed -n '/$var/!p'
A
B
C
A
D
E
Nous constatons que toutes les lettres sont affichées et c'est normal vu que la variable n'a pas été interprétée par le shell à cause des quotes simples.
Maintenant, entourons l'expression avec des quotes doubles :
var=A; echo -e "AnBnCnAnDnE" | sed -n "/$var/!p"
-l: !p": event not found
Le shell nous renvoie un message d'erreur ! Effectivement, le "!" est un caractère réservé du shell qui sert notamment pour l'historique des commandes et qui n'a pu être interprété tel quel.
Dissocions donc la variable de l'expression propre à Sed en refermant l'expression juste avant la variable et en la rouvrant juste après à l'aide des quotes simples (notez que les quotes doubles autour de la variable sont facultatives, mais qu'il est préférable de les laisser, parce que c'est une bonne habitude à prendre et pour se prémunir d'éventuelles erreurs dues à des espaces au sein de la variable) :
$ var=A; echo -e "AnBnCnAnDnE" | sed -n '/'"$var"'/!p'
B
C
D
E
Les expressions régulières
La connaissance des expressions régulières (regex en abrégé) est un petit plus non négligeable dans la pratique de Sed (et de bien d'autres langages).Une expression régulière est un motif (pattern en anglais) qui entre en correspondance, de la gauche vers la droite, avec une chaîne de caractères.
Les expressions régulières tirent leur puissance de leurs capacités d'inclure des alternatives et des répétitions dans l'élaboration du modèle constituant le motif. Ces spécificités sont élaborées à l'aide de caractères spéciaux, qui ne sont pas interprétés au sens littéral du terme, mais d'une façon spécifique.
Voici une brève description de la syntaxe des expressions régulières utilisée dans Sed.
caractère (un caractère quelqconque)
- Correspondance avec un "caractère" unique
*
- Correspond à une séquence de zéro, une ou plusieurs occurrence(s) de l'expression précédente, qui peut être un caractère ordinaire, un caractère spécial protégé par un \, un point (.), un groupe d'expression régulière ou une sous-expression
\+
- Identique à *, à part qu'il correspond à une ou plusieurs occurence(s) de l'expression précédente
\?
- Identique à *, à part qu'il correspond à zéro ou une occurence de l'expression précédente
\{i\}
- Identique à *, à part qu'il correspond à exactement i séquence de l'occurence de l'expression précédente (i représente un entier)
\{i,j\}
- Correspond à une séquence inclusive comprise entre i et j
\{i,\}
- Correspond à une séquence plus grande ou égale à i
\(regexp\)
- Correspond à un ensemble d'expression régulière, appelé aussi sous-expression et pouvant être adressé par référence arrière (back reference)
. un point
- N'importe quel caractère, excepté une nouvelle ligne
^
- Correspond à une chaîne nulle en début de ligne, autrement dit ce qui se trouve après l'accent circonflexe doit apparaître en début de ligne ou au début d'une sous-expression
$
- Idem que ^ mais la correspondance se fait en fin de ligne ou de sous-expression
[liste]
- Correspond à n'importe quel caractère de la liste. Une liste peut être constituée d'une séquence comme [a-z] ce qui équivaudra à une mise en correspondance avec n'importe quel caractère compris entre a et z inclus. Pour inclure un ] faites-le figurer en première position dans la liste. Pour inclure un - mettez-le en première ou dernière position dans la liste. Pour inclure un ^ mettez-le après le premier caractère de la liste.
Les caractères $, *, ., [, et \ ne sont pas normalement considérés comme des caractères spéciaux à l'intérieur d'une liste. Ainsi l'expression [\*] correspondra autant au caractère \ qu'au caractère *, le \ n'étant pas vu comme un caractère d'échappement servant à protéger le caractère *.
[^liste]
- A l'inverse, un ^ en début de liste, fera correspondre n'importe quel caractère excepté ceux de la liste. Pour inclure un ] faites-le figurer en première position dans la liste juste après le ^.
regexp1\|regexp2
- Correspondance avec regexp1 OU regexp2. Le processus de correspondance essaye chaque alternative à son tour, de gauche à droite et la première qui réussit est utilisée. Notez le caractère d'échappement (\) pour protéger le caractère "pipe" (|).
\nombre
- Correspond à la énième sous-expression \(...\) utilisée dans l'expression régulière. Les sous-expressions, appelées aussi "références arrières", sont implicitement numérotées de gauche à droite en comptant le nombre d'occurrences de "\(", avec un maximum de 9.
\n
- Correspond au caractère nouvelle ligne (LF)
\métacaractère
- Correspond à un métacaractère parmi $, *, ., [, \, ou ^, devant être protégé pour un interprétation littérale.
Note :
- Attention, les expressions régulières sont très gourmandes. Lors d'une mise en correpondance, celle-ci est effectuée de la gauche vers la droite, et si deux (ou plus) expressions ont le même caractère de départ, la correspondance sera faite sur l'expression la plus longue.
Les caractères d'échappement
\a- alerte (cloche bip) (BEL, Ctrl-G, 0x07)
\b
- effacement arrière (BS, Ctrl-H, 0x08)
\f
- fin de page (FF, Ctrl-L, 0x0C)
\n
- fin de ligne (LF, Ctrl-J, 0x0A)
\r
- retour chariot (CR, Ctrl-M, 0x0D)
\t
- tabulation horizontale (HT, Ctrl-I, 0x09)
\v
- tabulation verticale (VT, Ctrl-K, 0x0B)
\o000
- le caractère dont la valeur en octal est 000 (un à trois chiffres) [0-7]
\dDDD
- le caractère dont la valeur en décimal est DDD (un à trois chiffres) [0-9]
\xHH
- le caractère dont la valeur en hexadécimal est HH [0-9A-F]
Les extras
\'- correspond à une chaîne nulle en début de ligne (identique à ^)
'
- correspond à une chaîne nulle en fin de ligne (identique à $)
\b
- correspond à une chaîne vide à l'extrémité d'un mot. Limite entre un mot et un caractère autre qu'un mot
\B
- correspond à une chaîne vide ne se trouvant pas à une extrémité de mot. Limite entre un caractère autre qu'un mot et un mot
\w
- n'importe quel mot de la classe : [A-Za-z0-9_] (underscore _ compris)
\W
- n'importe quel mot en dehors de la classe : [^A-Za-z0-9_] (underscore _ compris)
\s
- n'importe quel caractère d'espacement : espace, tabulation horizontale ou verticale
\S
- un ou plusieurs caractères d'espacement
\<
- correspond à une chaîne vide en début de mot
\>
- correspond à une chaîne vide en fin de mot
\e
- fin de conversion de casse
\l
- conversion du prochain caractère en minuscule
\L
- conversion des caractères restant en minuscule
\u
- conversion du prochain caractère en majuscule
\U
- conversion des caractères restant en majuscule
Les classes de caractères
[:alnum:]- caractères alphanumériques [A-Za-z0-9]
[:alpha:]
- caractères alphabétiques [A-Za-z]
[:digit:]
- chiffres [0-9]
[:lower:]
- caractères minuscules [a-z]
[:upper:]
- caractères majuscules [A-Z]
[:print:]
- caractères imprimables [ -~]
[:punct:]
- caractères de ponctuation [!-/:-@[-'{-~]
[:space:]
- espaces, tabulations et tout caractère vide [ \t\v\f]
[:blank:]
- espace et tabulation [ \x09]
[:graph:]
- n'importe quel caractère imprimable [!-~] (excepté les espaces vides)
[:cntrl:]
- caractères de contrôle [\x00-\x19\x7F]
[:xdigit:]
- chiffres hexadécimaux [0-9a-fA-F]
Les différentes versions
Unix
- GNU sed v4.0.5 (Gsed) Téléchargement
- Dernière version officielle de GNU sed offrant l'édition en place (-i)
- Ssed v3.60 Téléchargement
- (Small/Stupid Stream EDitor) Version recommandée
- BSD multi-byte sed Téléchargement
- (Japonnaise) Basée sur le dernière version de GNU Sed
Windows
- GnuWin32-Sed v4.1.5 Téléchargement
- La version exécutable pour MS Windows 95 / 98 / ME / NT / 2000 et XP
Debuggers
Voilà 2 debuggers qui vous permettront de mieux comprendre le fonctionnement de Sed et, dans quelques cas particuliers, vous économiseront de nombreuses heures à vous arracher les cheveux en essayant de trouver où se situait le grain de sable qui obstruait votre joli filtre devenu tout à coup imperméable...- http://sed.sourceforge.net/grabbag/scripts/sd.ksh.txt et http://sed.sourceforge.net/grabbag/scripts/sd.sh.txt sont 2 petits scripts écrits respectivement en Korn shell et Bourne shell qui incluent un manuel d'utilisation en fin de fichier.
- sedsed est écrit en Python (qui doit donc être installé sur votre système) et vous permet de visualiser l'état des tampons et les commandes interprétées au fur et à mesure.
Voilà un mini tutoriel pour l'emploi de "sedsed".
Usage : sedsed OPTION [-e sedscript] [-f sedscriptfile] [inputfile]
OPTIONS :
-f, --file lecture des commandes depuis le fichier désigné
-e, --expression permet d'enchaîner plusieurs commandes à la suite
-n, --quiet demande implicite de ne pas afficher l'état de la mémoire principale
--silent alias pour --quiet
-d, --debug active le mode debugage
--hide cache certaines options de débogage (options : PATT,HOLD,COMM)
--color active la sortie du débogage colorée (activée par défaut)
--nocolor désactive la sortie du débogage colorée
--dump-debug listage du débogage à l'écran
-i, --indent indentation du script, une commande par ligne
--prefix indentation préfixée par des espaces ou des tabulations (4 espaces par défaut)
-t, --tokenize mode verbeux, affiche chaque commande avec plus d'informations
-H, --htmlize convertit un script sed en une jolie page HTML colorée
-V, --version affiche la version du programme et quitte
-h, --help affiche une page d'aide et quitte
Signification en sortie :
PATT : Affiche le contenu de l'espace de travail (mémoire principale)
HOLD : Affiche le contenu de l'espace annexe (mémoire secondaire)
COMM : La commande SED devant être exécutée
$ Délimite le contenu de PATT et HOLD
La syntaxe la plus courante reste le mode débogage simple (-d) :
echo -e "AAAnBBBnCCCnDDD" | sed '/BBB/ {n;s/C/Z/2}'
echo -e "AAAnBBBnCCCnDDD" | sedsed -d '/BBB/ {n;s/C/Z/2}'
Schéma : debogage_-d.png
En mode indentation :
sedsed -i -n ':notag;/tag/!{1!H;1h;x;s/n/ /g;x;$b lastline;d;};/tag/{x;/^$/!p;$b lastline;d;b notag;};:lastline;x;p;'
Schéma : debogage_indent.png
Cacher l'affichage de l'espace annexe :
echo -e "AAAnBBBnCCCnDDD" | sedsed -d --hide=HOLD -n '/BBB/ {n;s/C/Z/2p}'
Quand ne dois-je pas utiliser Sed ?
Quand des outils spécifiques existent déjà et effectuent la tâche plus rapidement et plus simplement, (syntaxiquement parlant).- Recherche de motif simple :
- grep "motif" fichier # sed -n "/motif/p' fichier
- Exclusion de motif simple
- grep -v "motif" fichier # sed "/motif/!d' fichier
- Recherche de motif et affichage de lignes de contexte avant/après
- grep -A1 -B1 "motif" fichier # sed -n '/motif/! {x;d;}; /motif/{x;p;x;p;n;p;x;}' fichier
- Numérotation des lignes
- cat -n fichier # sed -e '=' fichier | sed 'N;s/\n/\t/'
- nl fichier
- Suppression des sauts de lignes
- tr '\n' ' ' < fichier # sed ':boucle; N; $! b boucle; s/\n//g' fichier
- Suppression de caractères individuels
- tr -d "[w-z]" < fichier # sed 's/[w-z]//g' fichier
- Répetition de caractères
- echo "Booonjoouuur" | tr -s "ou" # echo "Booonjoouuur" | sed 's/oo*/o/g;s/uu*/u/'
- Transposition de caractères
- echo "ABCDEF" | tr "[A-F]" "[a-f]" # echo "ABCDEF" | sed 'y/ABCDEF/abcdef/'
- Formatage de fichiers, préférer les commandes :
- fold
- fmt
- par
Pour certaines tâches dont "awk" et "perl" s'acquittent de façon plus simple et plus rapide, comme par exemple :
- Compter des champs et des caractères
- Compter des lignes d'un bloc ou des objets d'un fichier
- Opérations mathématiques
- Calculer la longueur d'une chaîne
- Manipuler des données binaires
- Boucles sur tableaux ou listes
- etc.
Limites connues des différentes versions
Voici quelques limites connues sur les versions distribuées de Sed, dépendantes bien entendu de son matériel, de sa mémoire, de son système d'exploitation et des bibliothèques C utilisées lors de la compilation de Sed.- Longueur maximale d'une ligne
- GNU sed : sans limitation
- ssed : sans limitation
- Taille maximale des mémoires tampons (principale + secondaire)
- GNU sed : sans limitation
- ssed : sans limitation
- Nombre maximal de fichier pouvant être lu par la commande "r"
- GNU sed v3+ : sans limitation
- ssed : sans limitation
- GNU sed v2.05 : le total des lectures (r) et écritures (w) ne doit pas excéder 32
- Nombre maximal de fichiers pouvant être écrits par la commande "w"
- GNU sed v3+ : sans limitation
- ssed : sans limitation
- GNU sed v2.05 : le total des lectures (r) et écritures (w) ne doit pas excéder 32
- Taille limite d'un nom d'étiquette
- GNU sed : sans limitation
- ssed : sans limitation
- BSD sed : 8 caractères
- Taille limite du nom de fichier en écriture
- GNU sed : sans limitation
- ssed : sans limitation
- BSD sed : 8 caractères
- Nombre limite de branchements
- GNU sed : sans limitation
- ssed : sans limitation
Les références
Vous trouverez ci-dessous les ouvrages ainsi que les sites qui m'ont servi à élaborer ce document.Livres
- UNIX Shell : Guide de formation avec 160 exercices corrigés
- Langages de scripts sous Linux
- sed & awk, Second Edition)
Les liens
Débutants et initiés
- info sed
- man sed
- Sed - An Introduction and Tutorial
- THE SED FAQ
- sed, a stream editor
- HANDY ONE-LINERS FOR SED
Gurus
IRC
- Freenode
- irc://irc.freenode.net/#sed