sed (stream editor) permet de modifier ou de supprimer une partie d’une chaîne de caractères, par exemple pour remplacer un caractère par un autre dans un fichier, ou encore supprimer des chaînes de caractères inutiles. C'est un outil très puissant.
Pour utiliser sed, vous devez lui fournir une chaîne à traiter. Cette chaîne peut provenir :
En règle générale la syntaxe est de la forme
"s/[occurrence_cherchée]/[occurrence_de_substitution]/[comportement]"
Un exemple simple de substitution dans une liste de fichiers :
sed 's/occurrence_cherchée/occurrence_de_substitution/g' -i mes_fichiers*
Pour pouvoir traiter un fichier, il est nécessaire de lier le fichier à sed. Nous utiliserons la commande grep sous la forme :
grep occurrence /fichier/a/parcourir | sed ... \\ grep occurrence /fichier/a/parcourir | cut ... \\
ou encore même conjointement
grep occurrence /fichier/a/parcourir | cut ... | sed ...
Pour nos exemples nous allons définir une variable à l'aide de la commande export
export chaine="ceci est une chaine de caractères"
Ainsi nous définissons la valeur de la variable $chaine. Nous pouvons à présent appeler cette variable au besoin.
echo $chaine | sed -e "s/ /_/g" # Donnera en réponse ceci_est_une_chaine_de_caractères
mplayer mon fichier.avi
ne fonctionne pas tandis que mplayer mon_fichier.avi
fonctionnera
Nous voulons récupérer l'uid ainsi que le gid d'un utilisateur en cours.
Sous linux, l'utilisateur courant est défini dans la variable $USER
La preuve en est :
echo $USER
retourne l'utilisateur en cours
l'uid et gid de l'utilisateur est stocké dans le fichier /etc/passwd.
Dans un premier temps récupérons la ligne concernant notre utilisateur (ici Florent).
grep $USER /etc/passwd
parcourir le fichier /etc/passwd et retourner la ligne concernant le nom de l'utilisateur en cours
Donnera en réponse
florent:x:1000:1000::/home/florent:/bin/bash
Vous me direz : parfait ! Et bien nous avons notre uid et gid. Certes mais le résultat est difficilement exploitable vous en conviendrez. Nous allons donc séparer de façon distincte les deux valeurs. Il nous faut pour cela analyser le résultat de la commande précédente pour définir des règles de traitement :
ces différentes remarques vont nous permettre de fixer des délimiteurs, isolons donc la chaîne précédant les :: du reste de la chaîne
grep $USER /etc/passwd | sed "s/::/%/" |cut -d'%' -f1
Parcourir le fichier /etc/passwd et retourner la ligne concernant le nom de l'utilisateur en cours | remplacer (s) les :: par % dans le résultat | séparer le résultat au niveau du(des) délimiteur(s) '%' (-d) et afficher la première partie de cette séparation (-f1)
donnera
florent:x:1000:1000
Le principe est donc maintenant posé. Effectuons une dernière action afin de mettre en forme le résultat :
grep $USER /etc/passwd | sed "s/::/%/g" |cut -d'%' -f1 |cut -d'x' -f2 |sed -e "s/:\([0-9][0-9]*\)/UID=\1\n/" -e "s/:\([0-9][0-9]*\)/GID=\1/" # Donnera UID=1000 GID=1000
Décortiquons les différentes étapes du traitement :
Commande | Sortie | |
---|---|---|
origine | grep $USER /etc/passwd | florent:x:1000:1000::/home/florent:/bin/bash |
Étape 1 | sed "s/::/%/" | florent:x:1000:1000%/home/florent:/bin/bash |
Étape 2 | cut -d'%' -f1 | florent:x:1000:1000 |
Étape 3 | cut -d'x' -f2 | :1000:1000 |
Étape 4 | sed -e "s/:\([0-9][0-9][0-9][0-9]\)/UID=\1\n/" -e "s/:\([0-9][0-9][0-9][0-9]\)/GID=\1/" | UID=1000 GID=1000 |
plus simple :
sed -n -e "/`echo $USER`/s/[^:]*:[^:]*:\([^:]*\):\([^:]*\):.*/UID=\1\nGID=\2/p" /etc/passwd
Voila les étapes du traitement d'une chaîne de caractères avec quelques détails à ne pas oublier.
Une syntaxe intéressante peut s'avérer parfois très utile notamment pour des occurrences de type chemin de dossier /chemin/de/fichier. En effet la présence de caractères / peut poser certains problèmes. Afin de pallier cela il est possible d'utiliser une syntaxe différente que voici :
sed -e "s|/chemin/vers/un/fichier|/chemin/vers/un autre|"
ainsi les / seront correctement interprétés.
sed -e "s/\/chemin\/vers\/un\/fichier/\/chemin\/vers\/un autre/
autres caractères à échapper : " [ ] . / ? , mais pas les () qui doivent être échappées au contraire, seulement pour définir un groupe à mémoriser
Si vous désirez modifier des occurrences dans un fichier vous pouvez préférer l'option -i qui permet d'éditer un fichier. Ainsi :
sed -i "s/une occurrence/une autre/g" /un/fichier.txt
Va remplacer toutes les occurrences une occurrence par une autre dans le fichier /un/fichier.txt
sed traite les lignes individuellement (l'une après l'autre), il semble donc, au premier abord, incapable de joindre deux lignes séparées par un motif comportant le retour-charriot (\n). Heureusement cette difficulté n'est qu'apparente : il suffit de lui indiquer de traiter les deux lignes. Exemple : joindre à la ligne précédente chaque ligne commençant par un espace (soit substituer un espace simple aux deux caractères saut de ligne puis espace)
sed 'N;s/\n / /g;P;D;' fichier
Dans cet exemple "N" indique à sed de lire également la ligne suivante, "P" d'afficher le résultat et "D" d'effacer la ligne suivante (puisqu'elle a déjà été lue). Les différentes commandes sont séparées par des points-virgules.
Le texte suivant :
Aujourd'hui Ubuntu est incontestablement un beau succès. On ne peut que s'en réjouir. L'avenir appartient aux heureux utilisateurs de cette distribution.
devient :
Aujourd'hui Ubuntu est incontestablement un beau succès. On ne peut que s'en réjouir. L'avenir appartient aux heureux utilisateurs de cette distribution.
Et pour finir la commande à saisir pour transformer les fins de ligne DOS (\r\n] en fins de ligne UNIX (\n) :
sed 's/^M$//' fichier
Où "^M" représente \r.
Comment faire ? | Commandes | |
Remplacer la chaîne ancienneChaine par la chaîne NouvelleChaine dans le fichier toto.txt | sed -i 's/ancienneChaine/NouvelleChaine/' /toto.txt | |
Remplacer la ligne complète qui commence par la chaîne ancienneChaine par la chaîne NouvelleChaine dans le fichier toto.txt | sed -i 's/ancienneChaine.*/NouvelleChaine/' /toto.txt | |
Remplacer la ligne complète qui contient la chaîne ancienneChaine par la chaîne NouvelleChaine dans le fichier toto.txt | sed -i 's/.*ancienneChaine.*/NouvelleChaine/' /toto.txt | |
Remplacer dans chaque ligne contenant 'foo' la chaîne ancienneChaine par la chaîne NouvelleChaine dans le fichier toto.txt | sed -i '/foo/s/./ancienneChaine/NouvelleChaine/g' toto.txt |
Il peut-être utile de pouvoir appliquer notre commande sed sur les fichiers du répertoire courant, mais aussi dans ses sous-répertoires2), pour cela on fera :
grep -rl 'texte_à_rechercher_par_exple_ma_pomme' /repertoire_ou_commencer_la_recherche | xargs sed -i 's/ma_pomme/ma_poire/g'
Vous l'aurez bien compris, avec cette commande, souvent associée à la commande cut, vous pouvez foncièrement faire tout ce que vous souhaitez. N’hésitez pas à lire la documentation concernant l'usage et la syntaxe de ces deux commandes que vous trouverez en tapant respectivement man sed et man cut
Il ne vous reste plus qu'à intégrer ces connaissances à vos scripts shell
Contributeurs : flo|va-nu-pied, bcag2