L'éditeur de flux sed

Si vous maîtrisez vi, alors vous savez utiliser sed sans même le savoir ! En effet, les commandes de sed reprennent essentiellement celles de ed, vous savez, l'éditeur de texte orienté ligne dont vi est une extension visuelle pleine page !

La ligne de commande du programme sed

Là, le vocabulaire risque d'être compliqué ! Il ne faut pas confondre la ligne de commande du programme sed avec les commandes internes à sed. Ce paragraphe est dédié aux options de la ligne de commande du programme.

La syntaxe générale d'appel de sed est la suivante :

sed [-e commande] [-f scriptsed] [-n] [fichier]

sed ne modifie jamais le fichier qui lui est passé en entrée, son résultat est renvoyé sur stdout

  1. -e commande
    Cette option permet de spécifier une commande simple. Cette dernière doit être mise entre quotes, doubles ou simples, afin d'éviter que certains caractères présents dans les expressions régulières (en particulier l'étoile) ne soient évalués par le mécanisme de globbing du shell. Notons que l'utilisation de -e est facultative, il est tout à fait possible de mettre la commande directement.
  2. -f scriptsed
    Les commandes à appliquer sont placées dans le fichier de nom scriptsed
  3. -n
    Comme vous allez bientôt l'apprendre (j'aime à croire que je peux apprendre quelque chose aux gens :)), sed écrit chaque ligne du fichier en entrée, modifiée ou non, sur la sortie standard, à moins que l'option -n ne soit présente, auquel cas, seules les lignes à afficher explicitement par commande le seront. Il est possible d'obtenir le même résultat en plaçant le commentaire spécial #n dans un fichier script à exécuter par sed.

Les commandes de sed

Nous attaquons désormais la section consacrée à l'étude des commandes internes de sed, i.e. les commandes d'édition du texte elles-mêmes.

Un avertissement important !

Lorsque vous tappez un script sed, il vous est possible d'indenter les commandes à votre guise en les faisant précéder d'espaces et/ou de tabulations. Toutefois, il ne faut surtout pas laisser trainer d'espaces à la suite de vos commandes (trailing blanks en Anglais) sous peine de générer des erreurs de sed.

Syntaxe générale

La syntaxe d'une ligne de commande sed est la suivante :

portée commande options

La portée ou adresse d'une commande

Comme dans vi, nous retrouvons ici la notion de portée d'application d'une commande, c'est à dire l'étendue des lignes à laquelle la commande doit s'appliquer. Toutefois, la signification de la portée dans les 2 cas est inversée ! En effet, dans vi, si vous ne spécifiez pas de portée, la commande est exécutée uniquement sur la ligne courante alors que sed applique par défaut chaque commande à l'intégralité des lignes du fichier en entrée.

Pour résumer, en une phrase, dans sed la portée est restrictive alors que dans vi elle est extensive.

La portée peut recouvrir plusieurs formes :

Les portées (adresses) simples
Les portées doubles
Elles sont composées d'une paire d'adresses simples séparées par des virgules. Toutes les combinaisons sont possibles :

Il est important de noter que vous pouvez spécifier l'inverse d'une portée, c'est à dire, les lignes ne vérifiant pas la condition, en faisant suivre la spécification de portée par le caractère "!". Il est possible d'inverser tout type de portée, simple ou double.

Toutes les commandes peuvent accepter une portée simple, certaines accepteront en plus une portée double. Ceci sera précisé dans l'étude individuelle de chaque commande.

Les commentaires sed

Une ligne de commentaire est de la forme :

# texte de commentaire

J'espère que vous avez noté la présence d'un espace après le signe #. Si celui ci n'est pas obligatoire, il est fortement recommandé car certaines commandes « cachées » de sed sont associées à des commentaires spéciaux. En outre, il faut impérativement commencer les commentaires en première colonne.

Par exemple, et là, il s'agit d'une commande documentée, il est possible de remplacer l'option -n de la ligne de commande par le commentaire #n.

La commande d'affichage p

C'est la commande la plus simple de sed. Son but est d'afficher les lignes concernées par sa portée (qui peut être simple ou double) spécialement si l'option de ligne de commande -n est présente.

Par exemple, le tableau suivant présente, dans la colonne de gauche, le texte que nous allons traiter avec sed ; dans la colonne centrale, la commande sed que nous allons appliquer ; et pour finir, dans la dernière colonne, le résultat de l'application de sed -n commande texte qui nous montre bien que seules les lignes contenant l'expression régulière de portée sont affichées.

Texte initial Script Résultat
Un deux trois
Un deux
deux trois Quatre
trois Cinq Quatre
/trois/p Un deux trois
deux trois Quatre
trois Cinq Quatre

La commande sed -n -e "/re/p" fichier est équivalente à grep "re" fichier.

La commande l (un L minuscule) est une prochaine cousine de p à une ENORME différence près : les caractères non affichables sont représentés par la chaîne "\0code_ascii_octal", ce qui permet de les repérer facilement !

La commande de remplacement s

Assurément, c'est la commande la plus utilisée de sed ! En effet, elle permet de remplacer du texte par un autre avec des options particulièrement évoluées, en particulier, des expressions régulières dans le motif de recherche et le motif de remplacement. Son format générique est le suivant :

[portee]s sep motif_recherche sep motif_remplacement sep options

Commentons cette commande qui peut paraître compliquée au premier abord en décomposant chacun de ces éléments :

portee simple ou double
La commande de remplacement peut être utilisée avec toutes les variantes de portée supportées par sed.
sep
Ce caractère va servir à délimiter le motif de recherche du motif de remplacement et des options. Théoriquement, n'importe quel caractère peut servir de séparateur, à condition, bien entendu, d'utiliser trois fois le même dans la même expression .
Pour des questions de lisibilité, l'on utilise habituellement : "/" qui est également le seul caractère susceptible de délimiter une expression régulière définissant une portée, "+" ou "," si l'expression recherchée (ou le motif de remplacement) est susceptible de contenir des "/".
Si l'un des motifs contient le caractère de séparation, il est possible de le banaliser, dans le motif, en le précédant d'un backslash \
motif_recherche
Il s'agit d'une expression régulière étendue, (c'est à dire qu'elle peut contenir, par exemple, les méta caractères "+", ou "?" ainsi que des groupes) spécifiant le texte à rechercher.
motif_remplacement
Il s'agit également d'une expression régulière spécifiant le texte de remplacement. Cette expression régulière pourra contenir des méta caractères spécialisés permettant de récupérer du texte dans le motif de recherche.
options de recherche remplacement
Ces options permettent de modifier le comportement standard de la commande de recherche et remplacement.
Les options de répétition
Par défaut, le remplacement n'est effectué que sur la première occurrence du motif recherché sur chacune des lignes du fichier. Les options de répétition vont nous permettre de modifier ce comportement.
g
Le remplacement est global : le motif est remplacé autant de fois qu'il est présent sur la ligne.
n
n, entier compris entre 1 et 255, indique que seule la nième occurrence du motif de recherche doit être remplacée par le motif de remplacement
Les options de sortie du résultat
Elles conditionnent l'affichage réalisé par sed. Rappelons que par défaut, sed affiche toutes les lignes du fichier sur lequel il travaille, qu'elles soient modifiées ou non … à moins que l'utilisateur n'ait spécifié -n sur la ligne de commande.
p
provoque l'affichage des lignes affectées par la commande de remplacement. Si -n n'a pas été précisé sur la ligne de commande, les lignes affectées seront donc affichées 2 fois !
w nom_de_fichier
provoque l'écriture des lignes affectées par la commande de remplacement dans le fichier de nom nom_de_fichier. Si nom_de_fichier existe déjà, il est écrasé par la première instruction w, les instructions suivantes ajoutant leur texte au fichier.

Le motif de remplacement

L'une des grandes forces des éditeurs de texte d'Unix (et sed ne déroge pas à la règle) réside dans leur capacité à traiter des expressions régulières dans les motifs de remplacement.

Les caractères significatifs sont les suivants :

& signe « et commercial »
Si le signe & est présent dans le motif de remplacement, alors, il sera substitué par le motif de recherche en entier
\# où # est un nombre entier
Cette commande permet de substituer une partie du motif de recherche délimitée par les couples \( et \). Le premier sous motif à pour numéro 1, c'est à dire qu'il sera désigné par \1.

Exemples :

Supposez que vous disposiez d'une structure de données avec trois champs nommés obc, tch et aze auxquels vous accèdez par notation pointée. Vous décidez alors que votre structure pourra ajouter dynamiquement des champs et que ceux-ci pourront être accédés par adressage associatif sur leur nom à l'aide de l'opérateur crochet. Typiquement l'expression variable.champ deviendra variable["champ"]. La commande de remplacement peut s'écrire facilement avec sed sous la forme :

"s/\.\(aze\|obc\|tch\)/[\"\1\"]/g"

Examinons chacune des composantes de cette commande :

portée
Elle est ici absente, l'on étudiera donc toutes les lignes du fichier
séparateur
Ici, un simple caractère slash /
Motif de recherche : \.\(aze\|obc\|tch\)
Il s'agit tout d'abord du caractère ., lequel se présente sous la forme \. afin de lui ôter son caractère particulier (joker de recherche). Vient ensuite un groupe d'alternatives entouré par les délimiteurs \( et \). Il est très important de banaliser le caractère d'alternative | en le préfixant parc \. Le groupe d'alternatives forme ainsi un sous motif qu'il sera possible d'exploiter dans le motif de remplacement.
Motif de remplacement
Il est constitué d'une partie statique : [\" où vous noterez le \ avant le " afin d'éviter que le " ne signale la fin de l'expression régulière. Vient ensuite la partie dynamique \1 qui sera remplacée par la valeur du groupe d'alternatives (le nom du champ) dans la première partie et ensuite une autre partie statique \"]
Options
Le g est une option de recherche qui signifie tout simplement que toutes les occurrences présentes sur une ligne devront être remplacées.

Utilisons dorénavant notre expression de remplacement sur le fichier suivant (que nous supposerons appelé essai.txt)

printf("%d",tlkp.aze);
lop[1].obc = var2.obc ;
Z = var3.tch + var3.tkp;
var1.aze = aze;

La commande sed sera la suivante :

sed -e "s/\.\(aze\|obc\|tch\)/[\"\1\"]/g" essai.txt

et son résultat :

printf("%d",tlkp["aze"]);
lop[1]["obc"] = var2["obc"] ;
Z = var3["tch"] + var3.tkp;
var1["aze"] = aze;

Vous noterez que nous avons eu le résultat attendu ! En particulier :

Autre cas aussi intéressant, nous avons un fichier présenté sous forme d'une liste de couples de la forme (a,b) – un couple par ligne – et nous souhaitons l'écrire (a,b) devient (b,a). Comment pouvez vous le faire ?

Afin d'écrire l'expression de remplacement, décomposons le problème : Il s'agit d'identifier deux expressions séparées par une virgule et entourées par des parenthèses puis de les échanger. Le fait que l'on manipule les deux expressions impose de les traiter comme des groupes. On obtient alors :

s/(\(.*\),\(.*\))/(\1,\2) devient (\2,\1)/

Les expressions recherchées sont tout simplement des chaînes quelconques identifiées comme la répétition du caractère joker autant de fois que nécessaire. L La virgule centrale sert à délimiter la droite de l'expression de gauche et la gauche de l'expression de droite.a grande difficulté lors de la construction de cette expression régulière concerne les parenthèses. En effet, certaines font partie du motif à rechercher, les autres (celles précédées par un \) sont des caractères de contrôles permettant d'identifier les groupes.

Bien que la commande précédente soit juste, il aurait été plus simple de l'écrire :

s/(\(.*\),\(.*\))/& devient (\2,\1)/

où la commande & remplace toute la chaîne reconnue par le motif de gauche. Ceci permet d'éviter d'utiliser à nouveau une construction avec le caractère \, lequel a tendance à nuire à la lisibilité de l'ensemble.

Un application sympa de sed permet de modifier l'extension d'un grand nombre de fichiers en utilisant également find et xargs. La construction d'une telle commande est fournie en exercice corrigé.

Notion d'espace de travail

Maintenant que vous êtes familiers avec 2 commandes simples, il est temps de s'attaquer à la notion d'espace de travail tant celle ci est centrale pour la compréhension de sed.

Soit un script sed contenant plusieurs commandes, comment sont elles évaluées, et dans quel ordre ? Dans le cas le plus simple, sed lit une ligne, la place dans son espace de travail et applique successivement sur cet espace de travail toutes les commandes dont la portée est compatible avec l'espace de travail. Une fois toutes les opérations passées en revues, le contenu du tampon de travail est affiché (à moins que l'option -n n'ait été présente sur la ligne de commande en l'absence de toute commande d'affichage).

Considérons par exemple, le script et le texte suivant :

Texte initial Script sed
Premiere ligne
Seconde ligne
Troisieme ligne vide
/Seconde/s/ligne/& vide/
/vide/p

Si nous étudions le script, la première ligne remplace la chaîne "ligne" par "ligne vide" sur toutes les lignes contenant le mot "Seconde" alors que la seconde affiche les lignes contenant le mot vide.

Etudions l'action du script pas à pas :

Tampon de travail avant la commande Commande Résultat Tampon après la commande Etat courant de l'affichage du script
  Lecture de la première ligne Première ligne  
  Première ligne /Seconde/s/ligne/& vide/ Non exécutée car ne vérifie pas la condition de portée : tampon non modifié Première ligne  
  Première ligne /vide/p Non exécutée car ne vérifie pas la condition de portée : tampon non modifié et aucun affichage Première ligne  
Première ligne Toutes les commandes du script ont été considérée : on passe à la ligne de fichier suivante. Si la ligne de commande n'avait pas contenu l'option -n, le tampon de travail aurait été affiché préalablement. Seconde ligne  
Seconde ligne /Seconde/s/ligne/& vide/ La condition de portée est vérifiée : il y a exécution de la commande de remplacement et modification du tampon Seconde ligne vide  
Seconde ligne vide /vide/p La condition de portée est vérifiée : il y a exécution de la commande d'affichage Seconde ligne vide Seconde ligne vide
Seconde ligne vide Toutes les commandes du script ont été considérée : on passe à la ligne de fichier suivante. Si la ligne de commande n'avait pas contenu l'option -n, le tampon de travail aurait été affichée préalablement, ce qui, dans ce cas, aurait fait double effet avec la commande p Troisième ligne vide Seconde ligne vide
Troisième ligne vide /Seconde/s/ligne/& vide/ Non exécutée car ne vérifie pas la condition de portée : tampon non modifié Troisième ligne vide  
Troisième ligne vide /vide/p La condition de portée est vérifiée : il y a exécution de la commande d'affichage Troisième ligne vide Seconde ligne vide
Troisième ligne vide
Troisième ligne vide Toutes les commandes ont été passées en revue, et il n'y a plus de ligne à lire : le script est terminé   Seconde ligne vide
Troisième ligne vide

Vous noterez les points suivants :

Les commandes d'ajout/insertion de texte

Ces commandes permettent d'ajouter un texte statique avant (insertion) ou après (ajout) une ligne spécifiée dans l'adresse. A ce propos, l'adresse doit être simple : numéro de ligne unique ou expression régulière. Il n'est pas possible de passer une adresse de type plage qui n'aurait d'ailleurs pas de sens. La syntaxe générale est la suivante :

portée [a|i]\
texte \
texte \
..
texte

Vous remarquez tout de suite que ces commandes sont sur plusieurs lignes, ce qui est plutôt rare pour sed. En fait la difficulté résidait dans la spécification du texte à ajouter. Celui-ci doit commencer sur la ligne suivant la commande, seule la dernière ligne ne devant pas se terminer par un backslash \. Si le texte à ajouter doit contenir un caractère backslash, celui-ci doit être doublé afin d'être banalisé.

Afin de clarifier notre propos, étudions l'effet de ces commandes. Pour ce faire, nous allons traiter le petit exemple suivant. Soit le texte initial :

Avant le debut
DEBUT
apres le 1er debut
avant le second debut
DEBUT
apres le second DEBUT
.. mais avant la 1ère FIN
est ce vraiment la FIN ?
cette fois c'est la FIN

Insérer du texte avant une ligne : la commande i

La syntaxe est la suivante :

/portée simple/i\ ... texte ...

Par exemple, la commande :

/^DEBUT/i\
/* Commentaire à placer avant le debut\
du bloc*/

... sur le fichier précédent (notez que nous ne traitons que les lignes ou le mot DEBUT est en début de ligne, donnera le résultat :

Avant le debut
/* Commentaire a placer avant le debut
du bloc*/
DEBUT
apres le 1er debut
avant le second debut
/* Commentaire a placer avant le debut
du bloc*/
DEBUT
apres le second DEBUT
.. mais avant la 1ere FIN
est ce vraiment la FIN ?
cette fois c'est la FIN

Ajouter du texte après une ligne : la commande a

La syntaxe est la suivante :

/portée simple/a\ ... texte ...

Par exemple, la commande :

/^DEBUT/i\
/* Commentaire à placer après la fin\
du bloc*/

appliquée au fichier précédent (notez que nous ne traitons que les lignes ou le mot FIN est situé au bout de la ligne) donnera le résultat :

Avant le debut
DEBUT
apres le 1er debut
avant le second debut
DEBUT
apres le second DEBUT
.. mais avant la 1ere FIN
/* Commentaire a placer apres la fin
du bloc*/
est ce vraiment la FIN ?
cette fois c'est la FIN/* Commentaire a placer apres la fin
du bloc*/

Cumulons dorénavant ces deux commandes dans un seul script :

/^DEBUT/i\
/* Commentaire a placer avant le debut\
du bloc*/
/FIN$/a\
/* Commentaire a placer apres la fin\
du bloc*/

Le résultat devient :

/* Commentaire a placer avant le debut
du bloc*/
DEBUT
apres le 1er debut
avant le second debut
/* Commentaire a placer avant le debut
du bloc*/
DEBUT
apres le second DEBUT
.. mais avant la 1ere FIN
/* Commentaire a placer apres la fin
du bloc*/
est ce vraiment la FIN ?
cette fois c'est la FIN/* Commentaire a placer apres la fin
du bloc*/

Si une ligne contient les 2 patterns de recherche, cela ne pose pas de problème spécial. Par exemple :

texte
DEBUT texte FIN
texte

devient :

texte
/* Commentaire a placer avant le debut
du bloc*/
DEBUT texte FIN
/* Commentaire a placer apres la fin
du bloc*/
texte

La commande de changement de texte c

La commande c de changement de texte ne doit surtout pas etre confondue avec la commande s de substitution. En effet, la commande s remplace une expression régulière par une autre, dans les lignes de sa portée, alors que c remplace son buffer de travail, c'est à dire en fait, sa portée, par son argument ! lequel est spécifié selon le même format que les commandes i ou a.

L'intérêt de cette commande réside dans le fait qu'une portée peut s'étendre sur plusieurs lignes, ce qui est dur à réaliser avec les substitutions. Du coup, la commande c est utilisée, dans la plupart des cas, avec des adresses doubles.

Considérons un exemple simple ou un fichier est constitué d'un entête quelconque se terminant par une ligne vide. Vous désirez remplacer tout cet entête par le texte blah pipo suivi d'une ligne vide, vous pourriez écrire la commande ainsi :

1,/^$/c\
blah pipo\

Exemple, l'application de la commande précédente au texte :

Ceci est un exemple
debile d'utilisation de la commande
de changement de texte

L'entete est termine
le texte commence !

Nous fournit le résultat :

blah pipo

L'entete est termine
le texte commence !

Notez également que la commande de changement de texte influe tellement sur le tampon de travail que les commandes suivantes ne sont pas appliquées.

La commande de suppression de texte

La syntaxe générale de cette commande est la suivante :

[portee] d

Cette commande détruit purement et simplement les lignes décrites dans sa portée. Dans le cas d'un script, il y a retour immédiat à la première instruction.

Une astuce à maîtriser absolument !

Le gros problème consiste à choisir le bon délimiteur pour les expressions régulières. Or, lorsque vous écrivez un script, vous ne pouvez pas savoir si tel ou tel caractère ne risque pas de se retrouver dans les motifs de recherche ou de remplacement. Aussi, le meilleur moyen consiste à utiliser un caractère « très régulièrement absent », par exemple, le caractère de code ASCII 1, où «ctrl-A».

Ici ce pose un nouveau problème : comment saisir sans danger ce caractère ? Nous contournons la difficulté par l'astuce suivante :

CTRL_A=`echo | tr '\012' '\001'`

Si vous n'avez pas compris de votre propre chef, je vous propose les explications suivantes :

On comprend donc que la commande précédente place dans la variable CTRL_A le résultat du remplacement d'un saut de ligne par le caractère 1 ! On peut alors utiliser le résultat précédent avec sed, par exemple :

sed -e "s${CTRL_A}motif_recherche${CTRL_A}motif_remplacement${CTRL_A}options

Le groupage de commandes

Il est possible de grouper des commandes en utilisant des accolades. La forme générale est la suivante :

[portee du groupe]{
premiere commande
seconde commande
}

Le format de présentation est assez rigide, l'accolade ouvrante doit suivre immédiatement la portée (facultative) et l'accolade fermante doit être isolée sur une ligne.

Etudions les intérêts de regrouper ainsi plusieurs commandes :

Application de plusieurs commandes sur une même portée

Il y a une différence énorme entre :

portee1 commande1
portee1 commande2

et :

portee1{
  commande1
  commande2
}

Pouvez voir laquelle ?

Dans le premier cas, la portée est évaluée pour chaque commande. Si, par malheur, commande1 modifie suffisament certaines lignes pour qu'elles ne soient plus reconnues par portee1, alors la commande2 ne sera pas appliquée dessus et réciproquement.

En revanche, dans le second cas, la portée est évaluée avant l'entrée dans le bloc de commandes, garantissant que toutes les commandes seront appliquées sur les lignes reconnues par portee1 avant l'entrée dans les bloc.

Imbrication de portées

Avec le groupement, il est possible de définir des imbrications de portées rafinant progressivement la plage des lignes d'application des commandes. La syntaxe générale est la suivante :

portee1{
portee1A commande1A
portee1B commande1B
...
portee2{
portee2A commande2A
...
}
}

Exemple : dans un fichier HTML, on peut insérer du texte préformaté en l'entourant des balises <PRE> et </PRE>. Supposons que l'on souhaite détruire les lignes blanches dans de tels blocs. Il est possible d'utiliser les groupes pour faire cela :

/<PRE>/,/<\/PRE>{
/^$/d
}

et le tour est joué !

Un exercice mêlant sed et scripts shell

Nous allons petit à petit raffiner un exercice pour montrer comment utiliser une commande simple de sed à l'intérieur d'une script shell.

Etape 1

On demande de construire un script shell nommé echange dont la ligne de commande est la suivante :

echange motif_initial motif_remplacement fichier_a_modifier

et appliquant le remplacement de motif_initial par motif_remplacement dans fichier_a_modifier. A la fin de l'opération, l'opération devra être effective dans fichier_a_modifier

Un résultat possible :

#!/bin/ksh

# test sur les parametres !
if (( $# != 3 ))
then
  # bon, on a affaire a un gland ...
  echo "Probleme de parametres, usage : $0 motif_initial motif_remplacement fichier_a_modifier
else

  # mise en place de quelques variables !
  initial=$1
  final=$2
  fichier=$3

  # toujours utiliser cette astuce 

  CTRL_A=`echo | tr '\012' '\001'`


  # les instructions suivantes visent a creer un nom de fichier inexistant
  # en rajoutant autant de fois qu'il le faut le numero de pid du script ($$)
  # au nom de fichier initial

  resultat=${fichier}$$
  while [[ -e ${resultat} ]]
  do
    resultat=${resultat}$$
  done

  # application de la commande

	 sed -e "s${CTRL_A}${initial}${CTRL_A}${final}${CTRL_A}g ${fichier} > ${resultat}
  mv -f ${resultat} ${fichier}
  
  # et le tour est joue !
fi

Tout ceci n'est guère difficile, il suffisait de penser à :

  1. Gérer le nombre de paramètres
  2. Utiliser l'astuce du caractère ^A pour générer un caractère de séparation correct
  3. Générer un nom de fichier temporaire ne risquant pas d'écraser celui existant

Etape 2

Modifiez le script précédent pour qu'il puisse attaquer plusieurs fichiers.

Comme nous avons pris la précaution d'associer des variables aux arguments, ceci ne devrait pas poser de problème particulier : il suffit de rajouter une boucle sur les noms de fichiers qui sont nécessairement les derniers paramètres. Dans le script qui suit, les lignes en italique sont celles qui ont subi des modifications ou ont été rajoutées.

#!/bin/ksh

# test sur les parametres !
if (( $# < 3 ))
then
  # bon, on a affaire a un gland ...
  echo "Probleme de parametres, usage : $0 motif_initial motif_remplacement fichier1_a_modifier [fichier2_a_modifier ...]
else

  # mise en place de quelques variables !
  initial=$1
  shift	 
  final=$1
  shift
  # toujours utiliser cette astuce 

  CTRL_A=`echo | tr '\012' '\001'`


  for fichier in $*
  do

    # les instructions suivantes visent a creer un nom de fichier inexistant
    # en rajoutant autant de fois qu'il le faut le numero de pid du script ($$)
    # au nom de fichier initial

    resultat=${fichier}$$
    while [[ -e ${resultat} ]]
    do
      resultat=${resultat}$$
    done

    # application de la commande

  	 sed -e "s${CTRL_A}${initial}${CTRL_A}${final}${CTRL_A}g ${fichier} > ${resultat}
    mv -f ${resultat} ${fichier}
  
    # et le tour est joue !
  done
fi

Vous comprenez mieux maintenant l'intérêt de toujours travailler avec des noms de variables plutôt qu'avec les paramètres positionnels ? la modification a été évidente !

Etape 3 : application à la transformation des fichiers du format MSDOS au format Unix

La principale différence entre les deux formats de fichier texte réside dans la marque de saut de ligne : ^J sous Unix et ^M^J sous DOS. Nous allons appliquer notre script précédent à la transformation d'un grand nombre de fichiers.

Tout d'abord, la ligne de commande sera : echange "^M" "" fichiers que vous pourrez utiliser, par exemple, dans le cadre du résultat de find regroupé avec xargs :

find racine predicats -print | xargs echange "^M" ""

NB : pour obtenir un ^M sous vi, il faut entrer la séquence CTRL-V CTRL-M, alors que sous emacs, il s'agit de CTRL-Q CTRL-M.

Pour conclure sur sed

Nous n'avons vu ici qu'une infime partie des possibilité offertes par cet outil formidable qu'est sed. Si vous devez aller plus loin, vous pouvez consulter la liste des sites ouaib suivants, ou le formidable bouquin (dont je me suis partiellement inspiré) Sed et awk,programmation avancée de Dale Dougherry publié chez O'Reilly.

Quelques liens relatifs à sed

Vous pouvez également consulter la liste des questions fréquemment posées sur sed.