Guide de survie pour l’utilisation de la commande SED

I. Présentation

Bonjour à tous, aujourd'hui je vous présente un "guide de survie pour l'utilisation de la commande sed sous Linux". Elle représente selon moi l'une des commandes les plus pratiques, mais aussi l'une des plus compliqués à maîtriser.

Les capacités de la commande sed sont clairement très utiles dans certains cas, comme dans la réalisation de scripts bash, ou simplement en l'utilisant dans votre terminal pour vous faciliter la vie. Elle peut faire des actions qui prendraient des heures à faire à la main ou via une interface graphique.

Pour le dérouler de ce tutoriel, nous allons à chaque fois interagir avec des fichiers que nous allons créer (donc nous utiliserons l'option -i), mais n'hésitez pas à lire la documentation "express" de cette commande avec sed --help

Tout au long de ce tutoriel, vous trouverez de courtes définitions pour préciser le rôle de certaines regex en fonction des cas évoqués.  

II. Action de suppression

A. Supprimer une ou plusieurs lignes selon leur numéro

Créons un fichier :

nano test.txt
first line 
second line 
third line 
fourth line 

Suppression de la première ligne du fichier avec sed :

sed -i '1d' test.txt

Suppression de plusieurs lignes à la fois, par exemple les lignes 2 et 4 du fichier original contenant nos quatre lignes.

sed -i '2d;4d' test.txt

On remarque ici que le point-virgule ";" fait ici office de séparateur et que la lettre d d'indicateur pour supprimer la bonne ligne. 

Si vous souhaitez supprimer plusieurs lignes en spécifiant un intervalle, utilisez une virgule. Par exemple :

sed -i '1,4d' test.txt 

L'exemple ci-dessus supprimera les lignes 1 à 4 de notre fichier, ce qui dans notre cas correspond à tout le fichier.

B. Supprimer une ligne selon une REGEX

On commence par créer notre fichier que l'on va utiliser comme exemple, nous allons opérer la suppression de toutes les lignes du fichier test.txt commençant par un "#".

nano test.txt
hello 
# world 
# it connect 
it connect 
google

Ensuite, on peut supprimer toutes les lignes qui commencent par "#" avec sed et l'expression régulière suivante :

sed -i '/^#/d' test.txt

Cette fois, nous avons supprimé toutes les lignes du fichier commençant par "#".

  • /^ signifie "Ligne qui commence par un ..." dans notre cas un "#"
  • /d représente le délimiteur de la regex (on ne cherche pas plus loin sur la ligne)

Bien sûr, les RegEx ou expressions régulières, sont un sujet à part entière. Ici, je vous propose un exemple pratique qui s'appuie sur un RegEx où "/^" permet de préciser le début de ligne, mais avec les expressions régulières les possibilités sont illimitées !

C. Supprimer une ligne en fonction de son "identifiant"

Pour cette démonstration, je vais utiliser un cas concret, à savoir comment interagir avec la crontab par l’intermédiaire de la commande sed

crontab -e 

Voici trois tâches d'automatisation basique, inscrivez-les dans votre crontab comme ci-contre. Comme vous pouvez le constater, les trois lignes sont identifiées par un numéro.

* * * * * date >> /tmp/cron_output # 001
* * * * * whoami >> /tmp/cron2_output # 002
* * * * * ls /tmp >> /tmp/cron3_output # 003
cd /var/spool/cron/crontabs

Dans cette exemple ci-dessous, nous allons supprimer la ligne (tâche cron associée au compte root) identifiée par l'ID 002.

sed -i '/002/d' root
cat root

Cela est très pratique notamment lors de la rédaction de script bash pour interagir avec la crontab ou tout autre fichier. Sed indente automatiquement les lignes même si vous supprimez une ligne entre deux autres (comme notre exemple).

III. La substitution avec sed

La substitution permet de remplacer un mot ou une expression par un(e) autre, soit parce que celle-ci présente une faute de frappe, de syntaxe ou d'orthographe.

A. Corriger une faute d'un mot

nano test.txt
it connect
sed -i 's/it connect/it-connect/' test.txt
  • s/ : le "s" signifie substitute, autrement dit "Remplacement" - Remplacer le mot commençant par "it connect".

Les slash font ici office de délimiteur, pour bien circonscrire le mot qu'on souhaite remplacer. 

B. Convertir le texte d'un fichier en majuscule

Pour cet exemple, je reprends le même fichier que plus haut.

sed -i 's/.*/\U&/' test.txt
  • .* : ici rien de compliquer cela signifie "tout le texte du fichier"
  • U : pour upper-case, soit majuscule en français

C. Convertir le texte d'un fichier en minuscule

Même formule, pour convertir l'ensemble d'un texte en minuscule, il suffit de changer le U en L (pour lower case).

sed -i 's/.*/\L&/' test.txt

D. Convertir un mot d'un fichier en minuscule

Dans cette exemple, nous allons rechercher et changer le nom "IT-CONNECT" pour écrire ce mot en minuscule.

nano test.txt
UN TUTO DE PLUS POUR IT-CONNECT
UN TUTO DE PLUS POUR IT-CONNECT
UN TUTO DE PLUS POUR IT-CONNECT
sed -i 's/IT-CONNECT/it-connect/g' test.txt
# Or
sed -i "s/IT-CONNECT/\L&/g" test.txt
  • /g : Appliquez le remplacement à toutes les correspondances trouvées dans le fichier, pas seulement à la première.

IV. La translittération

Sous ce terme un peu barbare se cache un concept assez simple à comprendre. La "translittération" désigne le fait de changer une lettre de l'alphabet pour une autre. Par exemple, vous avez un problème d’encodage avec les accents et vous voulez tous les faire sauter au profit de lettres "standard".

A. Remplacer tous les accents d'un fichier par des lettres standard.

Ici, nous allons remplacer tous les caractères éèêë du fichier test.txt par des "e".

nano test.txt
Les élèves aiment aussi bien la période de l'hiver que celle de l'été. 

être ou ne pas être ? 
sed -i 'y/éèêë/eeee/' test.txt
  • /y : cette option permet de rechercher uniquement lettre par lettre les correspondances dans le fichier (et non une string c'est-à-dire une chaîne de caractères correspondante à "éèeë")

B. Remplacer une chaîne de caractères par une autre dans un fichier

nano test.txt
Bonjour le monde 
Bonjour le monde 
Bonjour le monde

Dans notre fichier, on peut remplacer la chaîne "Bonjour le monde" par "Hello world" en couplant l'utilisation des options "s" et "g" :

sed -i 's/Bonjour le monde/Hello world/g' test.txt
cat test.txt

C. Remplacer la valeur d'une partie d'une ligne de manière récurrente dans un fichier

L'exemple ci-dessous est très pratique lorsque vous souhaitez interagir avec un fichier de configuration. Exemple : changer la valeur d'une ligne d'un fichier de configuration.

Prenons l'exemple d'un fichier .htaccess composé des directives suivantes :

nano .htaccess
order deny,allow
deny from all
allow from 192.168.1.1

Note : Pour votre culture générale, ce fichier .htaccess autorise seulement l'adresse IP 192.168.1.1 à accéder à un dossier X d'un serveur WEB (en fonction du dossier dans lequel est positionné ce fichier).

Dans notre exemple, nous souhaitons changer l'adresse IP de "192.168.1.1" vers "10.0.0.1" dans un premier temps :

sed -i "s/allow from [0-9,\.]*/allow from 10.0.0.1/g" .htaccess

  • [0-9,\.]*  : Indique qu'on remplace tout chiffre ou caractère après la directive allow from

Ce qui est incroyable ici, c'est que vous pouvez modifier la valeur de l'adresse IP ou de la chaine de caractère autant de fois que vous le souhaitez. C'est extrêmement pratique lorsque vous devez modifier des fichiers de configuration "brute".

sed -i "s/allow from [0-9,\.]*/allow from 172.16.0.1/g" .htaccess

D. Modifier la valeur d'une variable dans un script bash

Ceci peut fonctionner pour tous les langages, il suffit de changer légèrement la regex en fonction de la syntaxe de celui-ci pour la déclaration d'une variable. Commençons par créer un script avec quelques lignes :

nano script.sh 
#!/bin/bash
Var1="ofzer"
Var2="hello world"
echo $Var2

On rend notre script exécutable avec chmod...

chmod u+x script.sh

Enfin, on définit la valeur "Bonjour le monde" pour la variable Var2 :

sed -i 's/Var2=.*/Var2="Bonjour le monde"/' script.sh

./script.sh && cat script.sh

  • "Var2=.*" signifie : "Changer la valeur de la variable Var2". Par défaut la recherche est appliquée à tout le fichier. 

Changeons de nouveau la valeur de Var2 avec la valeur "IT-Connect" dans notre script "script.sh" :

sed -i 's/Var2=.*/Var2="IT-Connect"/' script.sh
./script.sh && cat script.sh

Je me répète, mais ce qui est très pratique ici, c'est le fait que l'on puisse changer la valeur de la variable avec sed non pas une fois, mais autant de fois que vous le souhaitez.

E. Commenter un ensemble de lignes qui commencent de la même manière :

Ici, nous allons commenter les lignes commençant par myTestVar="10101010" myTest2ndVar="01010101", afin qu’elles ne soient plus prises en compte lors de l’exécution du script.

nano test.sh
#!/bin/bash
myTestVar="10101010"
myTest2ndVar="01010101"
otherVar="hello"
txt="some text"
sed -i '/my/ s/^#*/#/' test.sh
# Or
sed -i '/^my/ s/^/#/' test.sh.

- /my/ : indique simplement que nous souhaitons localiser dans le fichier toutes les lignes commençant par "my"
- s/^#*/# : dit que dans le cas où celles-ci (les lignes) commenceraient par my, on juxtaposerait un # en début de ligne. 

F. Ajouter une virgule ou un point virgule à la fin de chaque ligne d'un fichier

Pour cet exemple, je reprends le même fichier, que pour l'exemple précédent. Dans ce premier exemple, on ajoute une virgule à la fin de chaque ligne :

sed -i 's/$/,/' test.sh

Dans ce second exemple, c'est un point-virgule que l'on ajoute à la fin de chaque ligne :

sed -i 's/$/;/' test.sh 
  • $ : Signifie "À la fin de la ligne"
  • /; : "À la fin de la ligne, ajoute une virgule."

V. Commandes groupées

Voici une astuce qui peut faire gagner un temps précieux. Il est possible d’exécuter plusieurs commandes à la fois en utilisant les accolades "{" et "}". Ici nous verrons un seul exemple, mais sachez que vous pouvez faire des tests de votre côté avec les autres commandes que nous avons vues jusque-là. Bien sûr, entre chaque instruction, insérez un ";" pour délimiter la portée de votre commande.

nano test.txt

Tintin et Haddoc aime l'été

Le fichier de test étant créé, on passe à l'exemple avec sed

# remplacer tous les "é" accentués par des "e"
# et réécrire "Haddoc" -> Haddock
sed -i '{y/é/e/;s/Haddoc/Haddock/g}' test.txt

VI. Scripting

Dans cette section, nous allons voir comment réaliser de petits scripts avec Sed, qui peuvent vous faire gagner un temps précieux si vous faites face à de nombreux fichiers.

A. Renommer des fichiers

Imaginez que vous disposez d'une bibliothèque de livres que vous avez récemment téléchargée, et les noms de ces fichiers contiennent des "_". Par exemple : "file_with_underscore_is_boring.pdf", et vous souhaiteriez enlever les underscores "_" et les remplacer avec de simples tirets.

Commencez par créer un dossier spécifique pour plus de clarté. On appellera ce dossier "test-folder", puis on se positionne à l'intérieur après l'avoir créé :

mkdir test-folder

cd test-folder


Pour simuler l'existence de notre bibliothèque de fichiers PDF, on va créer quelques fichiers PDF vides, mais avec des noms contenant un "_" :

touch file_with_underscore_is_boring.pdf
touch file_with_underscore_is_boring2.pdf
touch file_with_underscore_is_boring3.pdf
touch file_with_underscore_is_boring4.pdf
touch file_with_underscore_is_boring5.pdf
touch file_with_underscore_is_boring6.pdf

Ensuite, on crée une boucle "for" pour appliquer un traitement sur chaque fichier PDF trouvé, en l'occurrence l'exécution de la commande sed pour renommer le fichier :

for f in *.pdf; do fn=`echo $f|sed 's/_/-/g'`; mv "$f" "$fn"; done      
ls -l

B. Modifier du texte dans plusieurs fichiers à la fois

Ça, c’est vraiment quelque chose que tout le monde devrait connaitre (surtout les développeurs). Vous pouvez par exemple changer la valeur d’une variable/fonction dans des milliers 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 !

Restez dans le dossier test-folder créé précédemment, et créez trois fichiers PHP :

touch 1.php
touch 2.php
touch 3.php

Voici le contenu des trois fichiers :

<?php
myFunction();
?>

On va remplacer le nom de notre fonction "myFunction" par "newNameOfTheFunction" dans tous les fichiers PHP :

for f in *.php; do sed -i -e 's/myFunction/newNameOfTheFunction/' "$f"; done

Une utilisation intéressante de cette option est qu’il est possible de conserver une sauvegarde des fichiers originaux avant leurs modifications. Pour cela, il suffit de suffixer l’option -i avec .bak :

Cela vous permettra d'éviter des erreurs d'exécution et de tout casser :

for f in *.php; do sed -i.bak -e 's/myFunction/NameOfTheFunction/' "$f"; done

Vous aurez ainsi vos fichiers .php modifiés comme dans l'exemple précédent et une copie des fichiers avant modifications sous la forme suivante *.php.bak. Si tout est OK, il faudra penser à supprimer les fichiers .BAK  bien sûr.

VII. Cas où il n'est pas nécessaire d'utiliser SED

Sed est une commande très puissante, mais je pense que vous constatez que sa syntaxe reste complexe à appréhender, surtout si vous avez du mal ou que vous débutez avec les regex. Dans certains cas, ce n'est pas la peine de l'utiliser pour éviter de se casser la tête. Ci-dessous, je présente 3 situations qui selon moi ne nécessitent pas forcément l'utilisation de Sed.

A. Changer l'extension d'un groupe de fichier

Ci-dessous, nous allons changer l'extension des fichiers *.sh en *.txt :

touch file1.sh file2.sh file3.sh
find . -name "*.sh" -print0 | while read -d $'\0' file; do    mv $file "${file%.*}.txt"; done

B. Renommer des fichiers en changeant leur préfixe

Maintenant, nous allons prendre 4 fichiers que l'on va créer avec la commande touch pour ajouter un préfixe au nom de chacun de ces fichiers. On s'appuiera, comme pour l'exemple précédent, sur la commande mv pour ajouter le préfixe "PRE_".

touch fichierNum1 fichierNum2 fichierNum3 fichierNum4
for f in fichier* ; do mv -- "$f" "PRE_$f" ; done

C. Renommer des fichiers en changeant leur suffixe

Dans le même esprit que pour l'exemple précédent, on va créer 3 fichiers .TXT avec touch pour ensuite ajouter un suffixe après le nom. Ainsi, le nom file1.txt deviendra file1_english.txt : on s'appuie là encore sur une boucle for et sur mv pour ajouter ce morceau de texte.

touch file1.txt file2.txt file3.txt
for filename in *.txt; do mv $filename ${filename%.*}_english.txt; done;

J'espère qu'à travers la lecture de cet article, vous avez mieux compris le fonctionnement de cette commande qui est très utile (et utilisé). Ces différents exemples pourront, je l'espère, vous aider !

➡ Pour en savoir plus sur la syntaxe des regex que l'on peut utiliser avec sed  : Sed - Regex

Partagez cet article Partager sur Twitter Partager sur Facebook Partager sur Linkedin Partager sur Google+ Envoyer par mail

Geoffrey Sauvageot-Berland

Ingénieur d'état en cybersécurité dans une entreprise spécialisée. Généraliste, avec une préférence pour Linux (Debian/Ubuntu), le scripting (Bash/Powershell), le developpement WEB ainsi qu'un fort penchant pour l'ethical hacking. Fondateur du site internet "Le guide du sysops".

geoffreysb has 12 posts and counting.See all posts by geoffreysb

6 thoughts on “Guide de survie pour l’utilisation de la commande SED

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

 

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.