Découvrir la commande sed par l'exemple
Sed peut faire des choses qui prendraient des heures à faire avec une interface graphique. Exemple ? Renommer 1500 documents d’un coup ou encore modifier du texte dans des centaines de fichiers à la fois ! Introduction et résumé des commandes.
sed [OPTION]... {script-only-if-no-other-script} [input-file]...
# Commandes :
sed -e --> commande uniligne
sed -f fichier --> passage de commande par un script
sed -i --> modifie directement le fichier source au lieu de rediriger vers la sortie standard
Avant de commencer, sachez qu’une grande partie de la puissance de sed provient des expressions régulière. Si vous ne les connaissez pas ou n’êtes pas bien à l’aise avec ces dernières, jetez un coup d’œil à mon article sur les regex.
Créons un fichier de test, test.txt
, avec le contenu suivant :
Voilà un simple fichier de test
#ceci représente un commentaire
ici_un_possible_nom de fichier
pareil avec espaces
pareil-avec-tirets
Si on invoque sed sans argument, il retourne la totalité du texte.
$ sed '' test.txt
Voilà un simple fichier de test
#ceci représente un commentaire
ici_un_possible_nom de fichier
pareil avec espaces
pareil-avec-tirets
Jusque là c’est un peu inutile, voyons donc ce dont cette commande est capable.
Suppression
Supprimer une ligne selon son numéro
Il faut pour cela utiliser l’option d
, pour delete.
$ sed '1d;4d' test.txt
#ceci représente un commentaire
ici_un_possible_nom de fichier
pareil avec espaces
pareil-avec-tirets
Vous notez que le point-virgule “;” fait ici office de séparateur. On supprime donc les lignes 1 et 4 (la 4 ne contenant qu’un retour à la ligne).
On peut également spécifier un intervalle en utilisant la virgule : sed '1,4d' test.txt
supprimera les lignes 1 à 4.
Supprimer une ligne selon une REGEX
Toujours avec l’option d
.
$ sed '/^#/d' test.txt
Voilà un simple fichier de test
ici_un_possible_nom de fichier
pareil avec espaces
pareil-avec-tirets
Les slash servent de délimiteurs. On supprime ici toutes le lignes commençants par “#”.
On peut aussi l’utiliser avec des intervales. Cela supprimera toutes les lignes comprises entre les deux match (matchs inclus).
$ sed '/^#/d;/_/d' test.txt
Voilà un simple fichier de test
pareil avec espaces
pareil-avec-tirets
Cette fois, nous avons supprimé toutes les lignes comprises entre une ligne commençant par “#” et une ligne comportant “_”.
Plus puissant encore, on peut mélanger les deux ! Exemple, je veux supprimer la première ligne et toutes les lignes qui commencent par “#”.
$ sed '1d;/^#/d' test.txt
ici_un_possible_nom de fichier
pareil avec espaces
pareil-avec-tirets
Filtrage
C’est exactement l’inverse de ce que nous venons de faire. Ici on choisit de ne rien afficher par défaut. On veut par exemple n’afficher que les lignes qui commencent par dièse ? Easy, on va utiliser l’option silencieux -n
, avec la commande print p
:
sed -n '/^#/p' test.txt
#ceci représente un commentaire
Par ailleurs, on peut choisir de n’afficher qu’une ligne, ou une intervalle.
# juste la ligne 5
sed -n 5p hexdump
# de la ligne 5 à la 10
sed -n 5,10p
La substitution
La substitution permet de remplacer un motif par un autre. On vient par exemple de récupérer un document dans lequel toutes les occurrences de votre nom contiennent une faute. Rien de plus facile à corriger.
$ sed 's/George Dupond/Georges Dupont/' synthese-dsi.txt
Note : si vous voulez utilisez les REGEX, c’est tout à fait possible ! Pensez d’ailleurs à utiliser l’option -r
qui permet la syntaxe étendue.
Il peut arriver que vous vouliez remplacer des retours à la ligne. Il faudra pour cela utiliser l’option -z
. Par exemple, vous avez un fichier texte content ceci :
Ce
site
est
génial
!
La substitution avec l’option -z
permettra de remplacer les retours à la ligne.
sed -z 's/\n/ /g' test.txt
Ce site est génial !
La translitération
Sous ce terme un peu complexe se cache un concept assez simple à comprendre. La translitération veut dire changer de lettre, tout simplement. Par exemple, vous avez un problème d’encodage avec les accents et vous voulez tous les faire sauter. Here you go.
$ sed 'y/éèêë/eeee/'
Commandes groupées
Voici une possibilité qui peut faire gagner un temps précieux. Il est possible d’appliquer plusieurs commandes à la fois en utilisant les accolades “{“ et “}”.
# remplacer tous les "e" accentués par des "e"
# et réécrire "Dupond" avec un "t" aux lignes 3 et 4
sed '2,6 {y/éèê/eee/;s/Dupond/Dupont/g}' fichiersource.txt
# même chose mais sur les lignes qui commencent par "#"
sed '/^#/ {y/éèê/eee/;s/Dupond/Dupont/g}' fichiersource.txt
Évidemment, on peut également utiliser les regex dans la substitution.
Exemples concrets
Renommer des fichiers
Vous venez de télécharger la dernière saison de votre série préférée et tous les noms des fichiers sont rendus illisibles à cause des “-“ à la place des espaces ? Sachant que c’est pour éviter d’avoir des problèmes de compatibilité à cause des espaces, vous préféreriez les remplacer par des underscore (“_”), qui sont plus lisibles.
$ for f in *.mp4; do fn=`echo $f|sed 's/-/_/g'`; mv "$f" "$fn"; done
Nous avons ici utilisé sed
conjointement à une boucle for do done. Bien entendu, nous sommes ici dans le dossier contenant les videos. Voici ce que fait ce code de manière détaillée.
for f in *.mp4;
on créé une variablef
, qui prend en paramètre le nom de tous les fichiers qui se terminent par .mp4 ;do fn=
on créer une nouvelle variableecho $f|sed 's/-/_/g'
;fn
, qui prendra comme valeur le résultat de la sous-commandeecho $f|sed 's/-/_/g'
. Les expressions entre backquotes (`) sont substituées par leur résultat en bash.
echo $f|sed 's/-/_/g'
on envoie à sed le contenu de$f
en combinantecho
et le pipe (|
) afin qu’il le mette au bon format.mv $f $fn;
enfin, on modifie le titre avec la commandemv
: change$f
par$fn
.done
, on indique que la commande est terminée.
Modifier du texte dans plusieurs fichiers à la fois
Ça c’est vraiment quelque chose que tout le monde devrait connaitre (on ne parle même pas des développeurs…). Vous avez par exemple une centaine de fichiers avec une même information qui vient de changer… Vous n’allez tout de même pas les ouvrir un par un et les modifier manuellement. Sed est là pour ça !
# exemple avec des fichiers php, on admet être dans le bon répertoire
$ for f in *.php; do sed -i -e 's/ancienne-adresse/nouvelle-adresse/' "$f"; done
On utilise ici l’option -i
, qui modifie directement le fichier au lieu de rediriger vers la sortie standard. Une utilisation intéressante de cette option est qu’il est possible de conserver une sauvegarde du fichier avant modification. Pour cela, il suffit de mettre un suffixe à l’option -i
:
$ for f in *.php; do sed -i.bak -e 's/ancienne-adresse/nouvelle-adresse/' "$f"; done
Vous aurez ainsi vos fichiers .php modifiés, et les fichiers avant modification seront de la forme .php.bak
.
Vous pouvez également rediriger tous les nouveaux fichiers (donc ceux modifiés) dans un nouveau répertoire en utilisant simplement un flux de redirection >
. Créons donc le dossier new.
$ for f in *.php; do sed 's/ancienne-adresse/nouvelle-adresse/' "$f" > new/"$f"; done
Je ne pense pas avoir besoin de vous expliquer ici comment marche la commande ! Tous vos fichiers modifiés se trouveront dans le dossier new, tandis que les anciens n’auront pas bougé.
Même parmi les développeurs, nombreux sont ceux qui ne savent pas se servir de sed
. C’est vraiment dommage car cette commande permet un énorme gain en productivité. N’hésitez pas à partager cette article autour de vous. Vos amis et collègues vous en sauront gré !
Par ailleurs, si la ligne de commande vous intéresse, jetez un œil mon article sur les commandes Linux les plus pratiques.
Commentaires
Thierry dit –
Merci pour ces explications. Une petite correction orthographique : sauront gré et non seront gré
Buzut dit –
Bien vu pour la typo ! Merci beaucoup.
Ahmed dit –
Salut!
Très bel article! Je souhaite remplacer dans un fichier texte l'occurene --domain=DOMAINENAME (sachant que je connais pas DOMAINNAME ) par --domain=XYZ
Un peu d'aide serait très apprécié :) Merci
Rejoignez la discussion !