Grimoire-Command.es

GNU+Linux command memo

Memo_12 : sed (Stream EDitor) traitement d'un flux de caractères

D’après l’édition 11 des travaux d’Alain Leaper, 2018-04-10
Licence GPL

1. Présentation

A partir d’un flux de caractères en entrée, SED réalise un traitement vers un flux de sortie. Le flux d’entrée est issu d’un fichier ou du résultat d’une commande. L’unité de traitement est la ligne (pattern space) (1). Le fichier utilisé en entrée n’est pas modifié (2). Le flux de sortie est dirigé vers le canal de sortie standard (voir memo_7) par défaut l’écran ou, et c’est généralement le cas, redirigé vers un fichier.

Notes:

  • (1) : ce comportement par défaut peut être modifié par l’option N, détaillée plus bas

  • (2) : ne pas tenter de "rebouclage direct" : $ sed -e '' <monFier >monFichier le résultat ne sera pas celui attendu (nomFichier a toutes les chances d’être vide) !

2. Quelques options

Les options sont introduites par la commande -e et sont entourées de guillemets '' (ou "").

Une option peut être "vide".

Exemple en utilisant le canal d’entrée standard par défaut (i.e. le clavier) :

$ sed -e '' (1)
salut la compagnie! (2)
salut la compagnie! (3)
(4)
1 Entrée
2 Texte entré au clavier, suivi d’Entrée
3 Par défaut, le flux d’entrée est répété sur le canal de sortie
4 Ctrl+D pour sortir

2.1. Option p (print)

En reprenant le même exemple :

$ sed -e 'p' (1)
salut la compagnie! (2)
salut la compagnie! (3)
salut la compagnie! (4)
(5)
1 Entrée
2 Texte entré au clavier, suivi d’Entrée
3 Par défaut, le flux d’entrée est répété sur le canal de sortie
4 Impression due à l’option p
5 Ctrl+D pour sortir

2.2. Annulation de la répétition du flux d’entrée: -n

$ sed -n -e 'p' (1)
salut la compagnie! (2)
salut la compagnie! (3)
(4)
1 Entrée
2 Texte entré au clavier, suivi d’Entrée
3 Impression due à l’option p, la répétition est supprimée
4 Ctrl+D pour sortir

3. Utilisation avec le résultat d’une commande.

Commande echo pour générer un flux d’entrée.

$ echo -e "salut\ntout le monde" | sed  -e 'p'
salut
salut
tout le monde
tout le monde

L’option -e de echo permet la reconnaissance des fins de ligne (\n), voir memo_4.

Noter que l’unité de traitement (répétition et impression) est la ligne.

4. Utilisation d’un motif de sélection ou expression rationnelle

Revoir le Memo_7 à ce sujet.

Les caractères / permettent de délimiter un motif pour réaliser une sélection: /motif/.

$ echo -e "salut\ntout\nle monde" | sed -n -e '/out/p'
tout (1)
1 seule le ligne contenant le motif (ici out) est présente en sortie

4.1. Sortie du numéro de ligne contenant le motif, option =

$ echo -e "salut\ntout\nle monde" | sed -n -e '/out/='
2 (1)
1 la ligne numéro 2 contient le motif out

4.2. négation: "tout sauf le motif" option ! (devant p ou =)

$ echo -e "salut\ntout\nle monde" | sed -n -e '/out/!='
1
3 (1)
1 les lignes 1 et 3 ne contiennent pas le motif
$ echo -e "salut\ntout\nle monde" | sed -n -e '/out/!p'
salut
le monde
1 la ligne "tout", qui contient le motif a été éliminée de l’impression

4.3. sélection à partir du flux d’entrée, filtrage de la répétition: option d

Ne pas utiliser -n.

$ echo -e "salut\ntout\nle monde" | sed  -e '/out/d'
salut
le monde (1)
1 la ligne tout, qui contient le motif a été éliminée de la répétition

On obtient le même résultat qu’avec !p, mais on ne filtre pas au même niveau…

5. Utilisation d’un motif de substitution

L’option s par s/motif_1/motif_2 assure le remplacement de motif_1 par motif_2.

$ echo -e "abzzj\nxxxx\ncdefzz\nghizzzzklm\nvv" | sed -n -e 's/zz/ZZ/p'
abZZj
cdefZZ
ghiZZzzklm (1)
1 remplacement partiel

Le remplacement s’effectue pour le premier motif trouvé pour chaque unité de traitement (la ligne).

Pour que toutes les occurrences soient traitées, il faut utiliser l’option g (global)

$ echo -e "abzzj\nxxxx\ncdefzz\nghizzzzklm\nvv" | sed -n -e 's/zz/ZZ/pg'
abZZj
cdefZZ
ghiZZZZklm (1)
1 remplacement global
Dans les deux cas, seules les lignes ayant subi une substitution apparaissent en sortie (lignes xxxx et vvv supprimées).

De la même manière que la sélection, la substitution peut se faire au niveau du flux d’entrée, sans utiliser -n et p.

$ echo -e "abzzj\nxxxx\ncdefzz\nghizzzzklm\nvv" | sed -e 's/zz/ZZ/g'
abZZj
xxxx
cdefZZ
ghiZZZZklm
vv
Les lignes n’ayant pas subi de substitution apparaissent également en sortie (lignes xxxx et vv présentes). C’est généralement le comportement souhaité lorsqu’on effectue des modifications par rapport à un fichier.

6. Restriction des lignes prises en compte

Soit le fichier construit selon :

$ echo -e "ozzo\nppzzp\nzzq\nabzzj\nxxzz\ncdefzz\nghizzzzklm\nvvzz\nzz" > fic_1

Si on souhaite limiter le remplacement aux lignes 5 à 8 :

$ sed  -e '5,8s/zz/ZZ/g' < fic_1
ozzo
ppzzp
zzq
abzzj (1)
xxZZ
cdefZZ
ghiZZZZklm (2)
vvZZ
zz (3)
1 les lignes 1 à 4 ne sont pas modifiées
2 remplacement pour les lignes 5, 6,7, 8
3 la ligne 9 n’est pas modifiée

Remarques:

  • $ sed -e '5,+3s/zz/ZZ/g' < fic_1 conduit au même résultat (ligne 5 à ligne 5+3)

  • le signe $ désigne la dernière ligne du fichier : $ sed -e '5,$s/zz/ZZ/g' < fic_1 remplacement à partir de la ligne 5 jusqu’à la fin de fichier

7. Fusion de lignes

L’option N permet la fusion de plus d’une ligne dans l’unité de traitement.

Soit le fichier construit selon :

$ echo -e "aaa\nbbb\nccc\nddd\neee" > test.txt

7.1. 1er cas : une ligne est réservée puis fusionnée avec l’unité de traitement qui contient la ligne suivante

$ sed -n -e 'N;s/^./X/p' < test.txt
Xaa
bbb
Xcc
ddd

Les 2 lignes se comportent comme une seule, le 1er caractère (début de cette ligne) est modifié. Remarquez que la ligne 5 (eee) est réservée mais elle ne peut être fusionnée, elle n’est pas traitée.

7.2. 2e cas : une ligne est réservée après celle entrée dans l’unité de traitement

$ sed -n -e 'N;s/^./X/p;N' < test.txt
Xaa
Xcc
Xee

Les 2e (bbb) et 4e (ddd) lignes ne sont pas traitées (par rapport à la modification).

7.3. 3e cas : cas hybride (1er cas + 2e cas)

$ sed -n -e 'N;s/^./X/p;N' < test.txt
Xaa
bbb
Xdd
eee

La 3eme ligne (ccc) est réservée mais non traitée, car vient après celle entrée dans l’unité de traitement.

Autre approche, ne pas utiliser l’option p (sans -n, le flux d’entrée est répété sur le canal de sortie).

$ sed -n -e 'N;s/^./X/;N' < test.txt
Xaa
bbb
ccc
Xdd
eee

Un résultat "presque" équivalent (une ligne sur 4 modifiée) peut être obtenu par :

$ sed -n -e 'N;N;s/^./X/' < test.txt
Xaa
bbb
ccc
ddd
eee

Mais il faut "suffisamment" de lignes pour réaliser la fusion !

Avec une ligne de plus (iii) la fusion ggg, hhh, iii est possible et donc ggg devient Xgg.

$ echo -e "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii" > test_2.txt
$ sed -e 'N;N;s/^./X/' < test_2.txt
Xaa
bbb
ccc
Xdd
eee
fff
Xgg
hhh
iii

8. Utilisation d’un fichier script

La commande -f permet de faire référence à un fichier de commande (script).

Création d’un fichier de commande (cmd_1) applicable à l’exemple précédent :

$ echo "N;N;s/^./X/" > cmd_1
$ sed -f cmd_1 < test_2.txt
(1)
1 même résultat que précédemment

Il est possible d’inclure la partie sed -f dans le script.

Il faut d’abord trouver son sed via :

$ whereis sed
sed: /bin/sed

Création du fichier de commande (cmd_2) :

$ cat > cmd_2 (1)
#!/bin/sed -f (1)
N;N;s/^./X/    (1) (2)
$ chmod u+x cmd_2 (3)
$ cmd_2 <test_3.txt (4)
(5)
1 Entrée
2 Ctrl+D
3 Permettre l’exécution
4 Le nouveau script est utilisable
5 Même résultats que précédemment

9. Substitutions ordonnées à l’aide de sous-expressions (sous-chaînes)

Re-revoir le Memo_7 à ce sujet.

Ici, dans un motif donné, la sous-expression de rang un est utilisée par la chaîne de remplacement.

$ sed -n -e 's/xxx\(abcd\)zzz/uuu\1tt/p'
xxxabcdzzz
uuuabcdtt

Ensuite, dans un motif donné, les sous-expressione de rangs deux, puis un, sont utilisées par la chaîne de remplacement.

$ sed -n -e 's/xxx\(abcd\)zzz\(efg\)/\2vvv\1tt/p'
kkk xxxabcdzzzefg lmn
kkk efgvvvabcdtt lmn

Autre exemple :

$ sed -n -e 's/\(Dupont \)\(monsieur \)\(Durand \)/\3\2\1/p'
bonjour Dupont monsieur Durand coucou
bonjour Durand monsieur Dupont coucou

10. Quelques exemples pratiques

10.1. Insertion d’espaces devant chaque ligne d’un fichier

$ echo -e "aaa\nbbb\nccc\nddd\neee" > test.txt
$ sed -e 's/\(^.\)/   \1/' < test.txt > provis
$ mv provis test.txt
$ cat test.txt
aaa (1)
bbb
ccc
ddd
eee
1 3 espaces en tête de chaque ligne

Remarques :

  • il est nécessaire d’utiliser un fichier relais (ici provis).

  • {3} convient pour une sélection mais pas pour une substitution. Impossible donc de faire : sed -e 's/\(^.\)/ {3}\1/' < test.txt > provis

10.2. Modification de tous les fichiers d’un répertoires

Par exemple, appliquer la même modification que précédemment à tous les fichiers d’extension .txt du répertoire courant vers un sous répertoire.

$ mkdir ./provis
$ for var in *.txt; do sed -e 's/\(^.\)/   \1/' < $var > ./provis/$var; done
$ mv ./provis/* ./ (1)
1 Si on le souhaite, il ne reste plus qu’à "écraser" les fichiers du répertoire courant

10.3. Un exemple pris dans le Makefile principal des sources de Debian / Lenny

SUBARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \
                                  -e s/arm.*/arm/ -e s/sa110/arm/ \
                                  -e s/s390x/s390/ -e s/parisc64/parisc/ \
                                  -e s/ppc.*/powerpc/ -e s/mips.*/mips/ \
                                  -e s/sh.*/sh/ )

La valeur de la variable SUBARCH est le résult de la commande uname (dans un Makefile, précédé de shell) modifiée par sed

10.4. Exemple du même genre, utilisable à partir du shell courant

$ var1=abcd
$ echo $var1
abcd
$ var1=`echo "bonjour"| sed -e 's/abc/efgh/' -e 's/xyz/kk/' -e 's/bonjour/salut/'`
$ echo $var1
salut