Traitement xml shell script

Résolu
Bonjour,
J'ai un soucis!
J'essaye de traiter un fichier au format xml (balises) sans succès :s

Un fichier comporte plusieurs blocs séparer par des balises.
Voici le type de bloc:
<listitem>
  <variable name="Name">farwarx</variable>
  <variable name="Other Name">farwarx1</variable>
  <variable name="Other Name">farwarx2</variable>
  <variable name="Other Name">farwarx2</variable>
  <variable name="Fullname">Mr Farwarx</variable>
  <variable name="Description">Comment ca marche</variable>
</listitem>


Je veux avoir comme résultat:
farwarx;farwarx1,farwarx2,farwarx3;Mr Farwarx;Comment ca marche


J'ai essayé avec sed et grep et paste > problème sur les champs "Other Name".
Je tente avec awk et match sans succès!!!

Avec de l'aide, on avait presque réussi avec cette commande (sur une CentOs):
#awk '{if ($1=="<listitem>") ligne=""; else if ($1=="</listitem>") print ligne; else { match($0,/<variable name="([^"]*)">([^<]*)</,champ); ligne=ligne""(champ[1]==prev? ",": ";")""champ[2]; prev=champ[1]; }}' prev=toto FICHIER


Mais à mon grand regret, je n'ai plus accès à cette machine et awk n'a pas la même fonction match, il ne supporte pas le tableau!!!

Quelqu'un pour m'aider?
Configuration: Portable Dell
Ubuntu

4 réponses

  1. Modérateur
    Salut,

    jp@MDK:~ ssh$ cat farwarx
    <listitem>
      <variable name="Name">farwarx</variable>
      <variable name="Other Name">farwarx1</variable>
      <variable name="Other Name">farwarx2</variable>
      <variable name="Other Name">farwarx2</variable>
      <variable name="Fullname">Mr Farwarx</variable>
      <variable name="Description">Comment ca marche</variable>
    </listitem>
    
    jp@MDK:~ ssh$ sed 's/ *<[^>]*>//g' farwarx | sed '/^$/d' | sed ':z;N;$!bz;s/\n/;/g'
    farwarx;farwarx1;farwarx2;farwarx2;Mr Farwarx;Comment ca marche
    
    jp@MDK:~ ssh$

    ;-))
    0
    1. Je savais que ça serai toi jipicy qui répondrai ;)
      J'ai trouvé une solution 'alternative' et non optimisée... mais c'était urgent :S

      Ton résultat n'est pas totalement correct:
      farwarx;farwarx1;farwarx2;farwarx2;Mr Farwarx;Comment ca marche
      farwarx;farwarx1,farwarx2,farwarx2;Mr Farwarx;Comment ca marche

      Note les ',' pour les champs identiques.
      Il faudrait que je me mette sérieusement sur sed, awk etc...., trop puissant ces outils ;)

      Voici ma solution alternative:
      #!/bin/bash
      while read ligne;
      do
        if [ "$ligne" == "<listitem>" ]
        then
          liste=""
        elif [ "$ligne" == "</listitem>" ]
        then
          echo $liste
          Name=""
          Domain=""
          Fullname=""
          Description=""
          MailAddress=""
          ForwardMode=""
          ForwardAddress=""
          Account_enabled=""
        else
          lparse=`echo $ligne |sed -e 's/<variable name="//' -e 's/">.*//'`
          case $lparse in
            Name)
              Name=`echo $ligne |sed -e 's/.*">//' -e 's/<\/variable>//'`
            ;;
            Domain)
              Domain=`echo $ligne |sed -e 's/.*">//' -e 's/<\/variable>//'`
            ;;
            Fullname)
              Fullname=`echo $ligne |sed -e 's/.*">//' -e 's/<\/variable>//'`
            ;;
            Description)
              Description=`echo $ligne |sed -e 's/.*">//' -e 's/<\/variable>//'`
            ;;
            MailAddress)
              MailAddress=$MailAddress","`echo $ligne |sed -e 's/.*">//' -e 's/<\/variable>//'`
            ;;
            ForwardMode)
              ForwardMode=`echo $ligne |sed -e 's/.*">//' -e 's/<\/variable>//'`
            ;;
            ForwardAddress)
              ForwardAddress=`echo $ligne |sed -e 's/.*">//' -e 's/<\/variable>//'`
            ;;
            Account_enabled)
              Account_enabled=`echo $ligne |sed -e 's/.*">//' -e 's/<\/variable>//'`
            ;;
            *)
            ;;
          esac
          liste=$Name";"$Domain";"$Fullname";"$Description";"$MailAddress";"$ForwardMode";"$ForwardAddress";"$Account_enabled
          liste=`echo $liste | sed -e 's/;,/;/'`
        fi
      done < $1
      


      J'avais d'autres champs par rapport à l'exemple, mais le principe est le même.
      Et je l'exécute avec un fichier en paramètre.
      Pas rapide, je te l'accorde, mais il fait ce que je lui demande, faute de savoir sur sed et awk.....
      Je sais qu'il est moche, mais qu'en on a plusieurs centaines de blocs, on scripte quoi qu'il arrive ^^

      Merci.
      0
      1. Modérateur
        [tmpfs]$ cat farwarx
        <listitem>
          <variable name="Name">farwarx</variable>
          <variable name="Other Name">farwarx1</variable>
          <variable name="Other Name">farwarx2</variable>
          <variable name="Other Name">farwarx2</variable>
          <variable name="Fullname">Mr Farwarx</variable>
          <variable name="Description">Comment ca marche</variable>
        </listitem>
        <listitem>
          <variable name="Name">toto</variable>
          <variable name="Other Name">toto1</variable>
          <variable name="Other Name">toto2</variable>
          <variable name="Other Name">toto2</variable>
          <variable name="Other Name">toto2</variable>
          <variable name="Fullname">Mr Toto</variable>
          <variable name="Description">Comment ca marche bien</variable>
        </listitem>
        
        [tmpfs]$ cat test.sed
        #n
        
        \#<listitem>#,\#</listitem># {
        
        /"Name/{
        s/ *<[^>]*>//g
        s/$/%/
        h
        D
        }
        :z
        /r Name/{
        s/ *<[^>]*>//g
        H
        N
        D
        bz
        }
        /Fullname/{
        s/ *<[^>]*>//g
        s/^/%/
        H
        D
        }
        /Description/{
        s/ *<[^>]*>//g
        s/^/%/
        H
        D
        }
        g
        s/\n%\|%\n/;/g
        s/\n/,/gp
        s/.*//
        x
        d
        }
        
        [tmpfs]$ sed -f test.sed farwarx
        farwarx;farwarx1,farwarx2,farwarx2;Mr Farwarx;Comment ca marche
        toto;toto1,toto2,toto2,toto2;Mr Toto;Comment ca marche bien
        
        [tmpfs]$

        ;-))
        0
        1. Tu es trop fort ;)
          0
      2. J'essaye de comprendre ton fichier test.sed.

        Est-ce que le #n a une importance?
        Que signifie exactement \#<listitem>#,\#</listitem># {?

        Ensuite j'ai bien compris que lorsque il trouve une occurrence (/"Name"/{, il exécute ce qu'il y a en dessous jusqu'à l'accolade.

        s: substitution

        Pour l'occurrence qui se répète on utilise un 'label' (comme un Go To?).
        :z pour le le début du label
        bz pour revenir au label z

        J'ai traduit du man les autres options (H, N, D), mais je n'ai pas tout compris.
        J'ai l'impression que l'option D permet de passer à la ligne suivante.
        Pour le H, je dirai qu'il stocke le résultat dans une case d'un tableau temporaire.
        Pour le N, c'est comme H mais il va mettre la prochaine valeur dans la même case que la précédente.

        Et au niveau du dernier bloc d'instruction, il se retrouve avec une ligne de résultat, et la tu le parses pour obtenir le résultat désiré.
        Tu avais rajouté le caractère '%' pour différencier les ';' des ',' et le tour est joué.

        C'est ça?
        0
        1. Modérateur
          Est-ce que le #n a une importance?
          Oui. En fait c'est l'équivalent du "-n" en ligne de commande. On n'affiche le traitement que si c'est implicitement demandé, donc avec le flag "p".

          Que signifie exactement \#<listitem>#,\#</listitem># {?
          C'est ce qu'on appelle des intervalles d'adressage, en fait les commandes comprises entre { et } ne s'appliqueront qu'a chaque intervalle de lignes situées entre les 2 motifs.

          Ensuite j'ai bien compris que lorsque il trouve une occurrence (/"Name"/{, il exécute ce qu'il y a en dessous jusqu'à l'accolade.
          Oui.

          s: substitution
          Oui.

          Pour l'occurrence qui se répète on utilise un 'label' (comme un Go To?).
          :z pour le le début du label
          bz pour revenir au label z

          Oui

          J'ai traduit du man les autres options (H, N, D), mais je n'ai pas tout compris.
          J'ai l'impression que l'option D permet de passer à la ligne suivante.

          Euh... non pas exactement, en fait c'est plus complexe. La commande D supprime de l'espace de travail (mémoire principale où est stockée la ligne en cours de traitement), tout ce qui est compris entre le début de la ligne jusqu'au 1er caractère de fin de ligne symbolisé par "\n".

          En gros si tu as une ligne dans l'espace de travail qui ressemble à :

          blabla\nautre chose\nla fin

          et que tu appliques la commande D, il restera :

          autre chose\nla fin

          et le traitement (sur cette ligne) reprendra au début du script (et non pas à l'instruction suivante).

          Pour le H, je dirai qu'il stocke le résultat dans une case d'un tableau temporaire.
          En gros oui, on appelle ça la mémoire secondaire. La ligne en cours de traitement est ajoutée (au contraire de la commande "h" où là la ligne est envoyée dans la mémoire tampon en écrasant ce qui s'y trouve) au contenu déjà présent. Chaque fin de ligne étant matérialisée par un \n.

          Pour le N, c'est comme H mais il va mettre la prochaine valeur dans la même case que la précédente.

          Non, la commande N ajoute la ligne suivante à la suite de celle contenue dans l'espace de travail, à l'instar de la commande "n" qui elle écrase le contenu de l'espace de travail par la ligne suivante.

          Et au niveau du dernier bloc d'instruction, il se retrouve avec une ligne de résultat, et la tu le parses pour obtenir le résultat désiré.
          Exact, avec la commande "g" qui rapatrie le contenu de la mémoire annexe dans l'espace de travail en écrasant le contenu. La commande "G" faisant la me chose mais en ajoutant à la suite.

          Tu avais rajouté le caractère '%' pour différencier les ';' des ',' et le tour est joué.
          Yes, il me fallait bien un subterfuge pour faire la différence ;-))

          C'est ça?
          En gros oui ;-))

          Pour de plus amples renseignements, voir la FAQ sur sed ;-))
          0
        2. Modérateur
          Complément d'infos...

          Concernant :
          Que signifie exactement \#<listitem>#,\#</listitem># {?
          C'est ce qu'on appelle des intervalles d'adressage ou plages d'adresses, en fait les commandes comprises entre { et } ne s'appliqueront qu'a chaque intervalle de lignes situées entre les 2 motifs.

          En ce qui concerne les "\#", en fait les caractères délimiteurs par défaut sont normalement les slashs "/", mais comme ce caractère se retrouve dans le motif lui même et pour ne pas avoir à l'échapper avec un anti-slash, j'ai préféré initialiser un nouveau délimiteur (le dièse) et pour ce faire je l'introduis en le protégeant par un anti-slash "\#".
          Tu me diras que ça revient presque au même que si j'avais laissé le délimiteur par défaut, c'est vrai, mais dans le cas de plusieurs slash dans le motif (comme un chemin ou URL) ça devient vite illisible ;-(
          0
        3. @jipicyMerci ;)
          0