Newer
Older
cours-unix-shell / src / sed / README.md

sed - UNIX

Sed (pour Stream EDitor) est un éditeur non interactif de texte.
Il permet d'appliquer une certain nombre de commandes à un fichier puis d'en afficher le résultat (sans modifier le fichier de départ) sur la sortie standard.
Comme avec toute commande unix, il est possible de rediriger la sortie vers un fichier résultat.

Substitution

La commande suivante lit un fichier file.txt et affiche ses lignes sur la sortie standard en remplaçant la chaîne « Dilib » par la chaîne « DILIB ».

sed 's/Dilib/DILIB/' file.txt

Il est conseillé d'entourer les commandes par des apostrophes simples, pour éviter que le shell n'interprète les caractères spéciaux (.*[]^$\).

⚠️ Cette commande ne remplace que la première occurrence de « Dilib » sur chaque ligne du fichier.
S'il y en a deux, la deuxième ne sera pas remplacée, à moins d'utiliser l'option g (pour global) en fin de commande :

sed 's/Dilib/DILIB/g' file.txt

Si on veut faire deux remplacements sur la même ligne de commandes, on peut utiliser :

sed -e 's/Dilib/DILIB/g' -e 's/Jacques Ducloy/Monsieur Dilib/g' file.txt

⚠️️ Le -e est obligatoire pour distinguer la deuxième commande d'un nom de fichier qui n'en est jamais précédé.

📗 Les commandes sont effectuées sur chaque ligne dans leur ordre d'apparition, ce qui veut dire que « Jacques Ducloy » sera transformé en « Monsieur Dilib » après la substitution de « Dilib » par « DILIB », il restera donc des « Dilib » sur la sortie standard.

Rappel sur les expressions régulières

^ début de ligne
$ fin de ligne
. n'importe quel caractère
[] classe de caractères (exemples : [A-Z] correspond à toutes les lettres majuscules)
[^...] classe de caractères correspondant à n'importe quels caractères sauf ceux qui suivent le caractère ^
Exemple : [^:] correspond à tous les caractères sauf le :.
* le caractère précédant * répété de 0 à n fois
+ le caractère précédant + répété de 1 à n fois
? le caractère précédant ? présent de 0 à 1 fois

Expressions régulières

On peut utiliser des expressions régulières dans les chaînes à remplacer (donc, il faut banaliser les caractères spéciaux dans cette chaîne).

sed -e 's/Jacques D[uU][cC][lL][oO][yY]/Monsieur Dilib/g' fichier

📗 Le caractère * englobe autant de caractères qu'il peut, ce qui veut dire que

sed 's/ré.*duction/réduction/g' fichier

transformera le fichier

do ré mi fa sol la si duction ah bon duction la suite

en

do réduction la suite

et non en

do réduction ah bon duction la suite

📗 Le caractère / est utilisé par convention, mais c'est le caractère qu'on met après la commande s qui est utilisé comme séparateur des expressions régulières.
Ça signifie que lorsqu'on a des expressions contenant beaucoup de / (comme quand on traite du langage balisé comme le XML ou le HTML), on peut utiliser autre chose, comme #.

cat <<EOF | sed -e 's#</b>##g'
<b>Bold</b>
EOF
<b>Bold

Récupération

Les opérateurs \( et \) sauvent leur contenu et permettent leur récupération par l'utilisation de \1, \2, etc.

sed -e 's/^\([A-Z][A-Za-z]*\), \([A-Z][A-Za-z]*\)/\2 \1/' fichier

Remplacera les « Nom, Prénom » en début de chaque ligne du fichier (quand il en trouve) par « Prénom Nom ».

Fichier de commandes

Si vous avez beaucoup de commandes, vous pouvez les rassembler dans un fichier comme celui-là :

# Fichier "exemple.sed"
# Il ne peut y avoir de commentaires que dans un bloc au début du
# fichier.
s/É/\&Eacute;/g
s/À/\&Agrave;/g
s/Ç/\&Ccedil;/g

⚠️ Le et commercial & est un caractère spécial dans les expressions régulières (il sert dans $& à reprendre tout ce qui a été trouvé), il faut donc le banaliser.

Pour appeler ce fichier, il faut utiliser l'option -f :

sed -f exemple.sed fichier

Une autre solution consiste à en faire un script exécutable (à la manière d'un script shell) :

#!/bin/sed -f
# Table de transcodage de ISO-8859-1 vers HTML
# Fichier "exemple2.sed"
s/É/\&Eacute;/g
s/À/\&Agrave;/g
s/Ç/\&Ccedil;/g

Mais il ne faut pas oublier de lui donner les droits en exécution, pour pouvoir l'appeler ainsi :

chmod u+x exemple2.sed
./exemple2.sed fichier

⚠️ Un fichier de commandes sed doit toujours se terminer par un passage à la ligne, sinon la dernière commande n'est pas prise en compte!

⚠️ De la même manière, toutes les lignes à traiter doivent finir par un retour à la ligne (en particulier la dernière)!

⚠️ Le shebang ne peut pas utiliser /usr/bin/env sed -f, car env ne comprend pas l'argument -f.
Pour que le shebang marche, on est obligé de passer le chemin réel de la commande sed.
Malheureusement, il n'est pas le même dans toutes les distributions de Linux (et peut même varier suivant les versions d'une même distribution).
Pour le faire marcher dans votre environnement, utilisez which sed et utiliser le résultat de cette commande (auquel vous ajoutez -f).

Modification de fichier

La commande sed, comme beaucoup de commandes UNIX, affiche le fichier qu'elle modifie sur la sortie standard.
Mais parfois, on voudrait juste pouvoir modifier ce fichier.

Or, utiliser sed -e <commande> fichier > fichier ne fonctionne pas (ça peut en faire un fichier vide, par exemple). On ne peut pas écrire dans un fichier qu'on est en train de lire (surtout s'il est grand) en utilisant la redirection.

C'est pourquoi il existe une option -i (pour in place) qui permet de modifier le fichier qu'on traite directement.

sed -i -e <commande> fichier

Sélection de lignes

sed étant un éditeur de ligne généraliste, il comprend beaucoup d'autres commandes que nous ne détaillerons pas ici.
Mais il peut être très utile de connaître les commandes pour sélectionner des lignes dans un fichier (c'est plus précis, et dans certains cas plus facile que de combiner head et tail).

Pour sélectionner les lignes 5 à 8 d'un fichier en entrée standard: sed -n 5,8p.

$ cat <<EOF | sed -n 4,6p
> 1
> 2
> 3
> 4
> 5
> 6
> 7
> 8
> EOF
4
5
6

Référence