Forum Programmation.shell Découpage d'un fichier EML

Posté par  .
Étiquettes :
0
27
avr.
2011

Bonjour,

Je parses un email (format eml) en Bash.

Voici un exemple du contenu :

root@mta:~/stage# cat test.eml
Date: Wed, 27 Apr 2011 09:58:01 +0200 (CEST)
From: Dupond <dupond@reseau.loc>
To: admin@reseau.loc
Subject: test
Message-ID: <9fc22f81-5d3c-4c06-8237-e2c2f9f947c3@mta>
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable
MIME-Version: 1.0
X-Originating-IP: [192.168.2.57]
X-Mailer: Zimbra 7.0.1_GA_3105 (ZimbraWebClient - FF3.0 (Linux)/7.0.1_GA_3105)

Bonjour,

Je me permets de vous faire remarquer ce test de contenu d'Email. Là, c'est le contenu de mon email.

Pour récupérer les champs importants, j'ai fait un : cat test.eml | grep -e "To" -e "From" -e "Subject" -e "Date"

En revanche, j'aimerais maintenant ne récupérer que le contenu du mail, c'est à dire de "Bonjour," à "contenu de mon email.".

Je vois pas comment faire =/.

Un petit coup de main ?

Bonne journée

  • # Python + email

    Posté par  . Évalué à 4.

    En bash, c'est assez difficile de traiter des emails.
    Moi j'utilise plutôt python et le paquetage email.

    • [^] # Re: Python + email

      Posté par  (site web personnel) . Évalué à -1.

      Mince j'ai été grillé de quelques secondes...

    • [^] # Re: Python + email

      Posté par  (site web personnel) . Évalué à 4.

      En bash, c'est assez difficile de traiter des emails.

      Tout dépend de ce que tu veux faire: récupérer le corps de l'EMail n'est quand même pas si compliqué qu'il faille jeter son script shell pour transformer son programme en script Python.

      On tire au maximum parti de l'architecture d'UNIX lorsqu'on utilise le shell pour coordonner les traitements: presque tous les langages on un accès au shell, et on obtient une réutalisabilité de tous ces efforts.

      Pour la question de récuperer le corps, tu peux définir la fonction

      eml_get_body() {
        awk 'BEGIN{ flag=0 } {if(flag) print } /^\r?$/ { flag=1}'
      }
      

      et l'utiliser comme suit
      body=`eml_get_body < message.eml`
      

      Attention aux useless use of cat: au lieu de faire cat & pipe il vaut mieux utiliser un opérateur de redirection (les chevrons), comme dans

      grep -e '^To:' < message.eml
      

      ou l'exemple précédent.
  • # Parser en python ?

    Posté par  (site web personnel) . Évalué à 3.

    Bonjour,

    il existe déjà des classes toutes faites pour le faire en python : http://docs.python.org/library/email.parser.html

    En bash sinon, si l'entête est de taille fixe, un coup de tail et/ou de head devrait suffire non ?

  • # sed ?

    Posté par  . Évalué à 3.

    sed -n '/^$/,+1 p' test.eml
    

    /^$/ pour trouver la première ligne vide
    ,+1  pour passer à la ligne suivante
    p    pour afficher le reste
    
    • [^] # Re: sed ?

      Posté par  . Évalué à 1.

      Arf, cela ne me renvoie strictement rien :S.

      • [^] # Re: sed ?

        Posté par  (site web personnel) . Évalué à 2.

        Remplace l'expression rationnelle ^$ par ^\r$ mais la séquence \r ne marche pas avec tous les sed.

        • [^] # Re: sed ?

          Posté par  . Évalué à 1.

          Et donc tu rentres directement le caractère ^M. Dans un xterm, c'est avec la séquence 'C^v Enter'. Ça c'est compris par tous les sed.

      • [^] # Re: sed ?

        Posté par  . Évalué à 4.

        En fait l'idée est bonne. L'en-tête d'un mail n'est pas de taille fixe.
        En revanche, l'en-tête et le corps du mail sont séparés par une ligne vide. La commande sed indiquée devrait donc fonctionner…
        Le seul soucis est que dans un mail, les changement de lignes sont indiqués par des CR-LF, alors que sous Unix on utilise le LF seul. La ligne de séparation n'est donc pas considérée comme une ligne vide mais comme une ligne contenant le CR.
        Pour que cela fonctionne, il suffit de modifier la commande ainsi :

        sed -n '/^\r$/,+1 p' test.eml
        

        ou, si on veut prendre en compte aussi bien les fins de lignes CR-LF que LF
        sed -n '/^\r*$/,+1 p' test.eml
        
        • [^] # Re: sed ?

          Posté par  . Évalué à 2.

          Bien vu. Je n'ai pas vérifié avec un véritable email.
          Ta seconde syntaxe passe tout, ça couvre les besoins exotiques.

    • [^] # Re: sed ?

      Posté par  (site web personnel) . Évalué à 1.

      Ton script veut dire: sélectionne les lignes comprises entre une ligne vide (adresse /^$/)et la suivante ( adresse +1) et affiche les (opération p). Ce n'est pas du tout ce que veut l'OP. Tu l'as testé?

      • [^] # Re: sed ?

        Posté par  . Évalué à 3.

        sed -n '/^\r*$/,$ p' test.eml
        

        extrait le corps du mail.

  • # Pas de python

    Posté par  . Évalué à 1.

    Non, Python n'est pas envisageable, tout mon script est fait en Bash, un portage en python me prendrais trop de temps =/.

    Normalement oui, le début est fixe... Donc si je supprimes X lignes, ça peut marcher...

    Mais dans le cas où c'est aléatoire ?

    J'ai tenté un grep "nn", mais il ne me renvoie rien.

  • # Tentative

    Posté par  . Évalué à 3.

    Je pense comme les autres qu'il vaut mieux utiliser la librairie de Python pour le faire.

    Cependant : déjà tu devrais faire grep -e '^To: ' plutôt que grep -e "To:", sinon tu pourrais récupérer des lignes qui ne sont pas du tout un header.

    Et tu devrais séparer le contenu avant de faire le grep des headers. Ils sont séparés de manière très simple : c'est la première ligne vide. Donc quelque chose comme… grep -m1 -n -e '^$' pour obtenir le numéro de cette ligne vide, et ensuite tu peux tail/head.

    DLFP >> PCInpact > Numerama >> LinuxFr.org

    • [^] # Re: Tentative

      Posté par  (site web personnel) . Évalué à 2.

      Le couple grep & tail & head est une bonne idée. Pour rester avec awk, utilisé dans mon exemple de solution pour récupérer le corps, l'isolation du header se fait comme cela:

      eml_get_header()
      {
        awk '/^\r$/{exit}{print}'
      }
      
  • # en shell

    Posté par  . Évalué à 1.

    cat test.eml | sed -n -e '/^^M*$/,$ p'

    Attention, le ^M est bien le CR, ie le caractère de code ascii 0x0D. Dans un xterm+bash, c'est 'C^v Enter', avec emacs c'est 'C^q Enter', avec les autres je ne sais pas.

    • [^] # Re: en shell

      Posté par  (site web personnel) . Évalué à 1.

      Cette solution marche, mais elle a le petit défaut de montrer la ligne vide, qu'on peut cacher avec tail.

      Encore un useless use of cat ™ :)

      • [^] # Re: en shell

        Posté par  . Évalué à 1.

        ok: [...] | tail -n +2
        Mais on arrive dans le cosmétique, quand même ;)

  • # en bash

    Posté par  . Évalué à 2.

    unset body
    shopt extglob
    while read line
    do [[ $line == @(To|From|Subject|Date)* ]] && echo "$line"
       ((body)) && printf '> %s\n' "$line"
       [ -z "$line" ] && body=1
    done <email.eml
    

    ...
    ?

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.