grep n'est vraiment pas adapté pour résoudre ce problème.
Donc je propose d'utiliser sed (ok, je l'admet, j'utilise sed pour à peu près tout faire. Si j'avais un script pour faire mon café, il serait probablement en sed. Mais ça n'empêche pas que sed, c'est bien )
Dans un fichier (disons extract_to:_field.sed) :
/^To:/ {
h
n
:loop
/^[[:blank:]]/ {
H
n
b loop
}
x
s/\n/ /g
s/[[:blank:]]\+/ /g
p
q
} |
ensuite:
sed -nf extract_to:_field.sed nom_fichier_a_traiter |
Sur l'exemple, ça donne comme résultat :
To: moi@domain.com, toto@domain2.com, toto2@domain3.com, fgdfg@domain4.com, tructruc@domain5.com, luilui@domain5.com |
(c'est le seul test que j'ai fait. Il faut donner plus d'exemples si vous cherchez des comportements un peu tordus )
Sed utilise deux buffers : le pattern space, qui contient la ligne en cours de traitement, et le hold space, qui permet de sauvegarder des résultats précédents.
Rajoutons des commentaires :
Code :
/^To:/ {
h # on met le contenu du pattern space (cad, la ligne qui commence par "To:" ) dans le hold space
n # on met la ligne suivante dans le pattern space
:loop
/^[[:blank:]]/ { # si la ligne commence par un blanc
H # on concatène la ligne courante au hold space existant
n # on met la ligne suivante dans le pattern space
b loop # on boucle en retournant a l'etiquette loop
}
# on arrive ici lorsque la ligne ne commence pas par un blanc
# le hold space contient la totalite du champ To:, avec éventuellement
# des retours à la ligne.
x # on échange le contenu du hold et du pattern space
s/\n/ /g # on enlève tous les retours à la ligne
s/[[:blank:]]\+/ /g # on enlève des espaces inutiles
p # on affiche le résultat
q # on quitte sed, ce qui nous évite des désagréments
# s'il y avait des lignes commençant par To: plus bas
}
|
Regardons ce qu'il se passe lorsqu'on exécute le script sur l'exemple.
sed lit les lignes une à une. il ne fait rien tant qu'on ne rencontre pas de lignes qui commence par "To:". Lorsqu'on en rencontre une (on est à la ligne 1), on entre dnas le bloc de code. On met le contenu du pattern space dans le hold space (ligne 2). Ils contiennent donc :
pattern space = To: moi@domain.com, toto@domain2.com,
hold space = To: moi@domain.com, toto@domain2.com,
On arrive en ligne 3, on execute la commande sed n, c'est à dire que l'on lit la ligne suivante, et qu'on la met dans le pattern space. Le hold space ne change pas.
pattern space = toto2@domain3.com, fgdfg@domain4.com,
hold space = To: moi@domain.com, toto@domain2.com,
En ligne 4, on a une étiquette appellée "loop" (on peut utiliser n'importe quel nom, bien sûr). Ça ne modifie évidemment pas les buffers.
En ligne 5, on regarde si le pattern space commence par un blanc. Si c'est le cas, on entre dnas le bloc qui suit (entre la ligne 5 et 9).
C'est effectivement ce qui se produit dans notre exemple.
En ligne 6, on ajoute le contenu du pattern space au hold space. Entre ce que contenait auparavant le hold space et ce qui est ajouté, sed ajoute un retour à la ligne, \n.
pattern space = toto2@domain3.com, fgdfg@domain4.com,
hold space = To: moi@domain.com, toto@domain2.com,\n toto2@domain3.com, fgdfg@domain4.com,
En ligne 7, on lit la ligne suivante, et on la met dans le pattern space. Le hold space ne change pas.
pattern space = tructruc@domain5.com, luilui@domain5.com
hold space = To: moi@domain.com, toto@domain2.com,\n toto2@domain3.com, fgdfg@domain4.com,
En ligne 8, on a un branchement. On "saute" à l'étiquette appellée "loop" (bref : on boucle).
On arrive en ligne 5, et on vérifie de nouveau si le pattern space commence par un blanc. C'est le cas, on entre dans le bloc.
En ligne 6 et 7, on refait la même chose que précédemment. On a alors :
pattern space = Subject: truc
hold space = To: moi@domain.com, toto@domain2.com,\n toto2@domain3.com, fgdfg@domain4.com,\n tructruc@domain5.com, luilui@domain5.com
En ligne 8, on boucle de nouveau.
En ligne 5, on regarde encore une fois si le pattern space commence par un blanc. Ce n'est pas le cas cette fois ci : on n'entre pas dans le bloc.
On arrive ligne 15. On execute la commande sed x, qui échange le contenu du pattern sapce et du hold space. (note : dans ce cas précis, on aurait pu utiliser la commande g, qui copie le contenu du hold space dans le pattern space, cela n'aurait rien changé). On a donc :
pattern space = To: moi@domain.com, toto@domain2.com,\n toto2@domain3.com, fgdfg@domain4.com,\n tructruc@domain5.com, luilui@domain5.com
hold space = Subject: truc
En ligne 16, on enlève les retours à la ligne du pattern space. La commande s remplace ce qui est matché entre les deux premiers slashs (ici : \n, le retour à la ligne) par ce qui est entre le deuxième et le troisième slash (ici, un simple espace). Le g signifie que l'on veut remplacer toutes les occurences de l'expression : si on ne le met pas, sed n'enlèverait que le premier \n.
pattern space = To: moi@domain.com, toto@domain2.com, toto2@domain3.com, fgdfg@domain4.com, tructruc@domain5.com, luilui@domain5.com
En ligne 17, on enlève les espaces superflus. + signifie "une fois ou plus". Il faut le protéger avec un antislash.
pattern space = To: moi@domain.com, toto@domain2.com, toto2@domain3.com, fgdfg@domain4.com, tructruc@domain5.com, luilui@domain5.com
En ligne 18, on demande d'afficher le pattern space. Lors de l'appel à sed, on a utilisé le drapeau -n, pour qu'il n'affiche rien par défaut (si on ne le met pas, sed affiche le contenu du pattern space a chaque fois qu'il arrive en bas du script).
En ligne 19, on demande à sed d'arrêter de traiter le fichier immédiatement.
Le script fait quelques suppositions qui peuvent poser problèmes; en particulier, il considère que les lignes du champs To: sont indentés lorsqu'il s'étend sur plusieurs lignes, et que la ligne suivant ce champ ne commence pas par un caractère d'espacement. Il faut aussi qu'il n'y ait pas de lignes qui commencent par "To:" avant celle qui nous intéresse, et corrolaire, il ne peu pas y avoir plusieurs champs "To:". Ça me semble plutôt raisonnable, mais j'ignore si c'est valide, et je n'ai franchement pas envie de subir la/les RFCs adéquates