Traitement xml shell script

[Résolu/Fermé]
Signaler
Messages postés
125
Date d'inscription
vendredi 30 mai 2008
Statut
Membre
Dernière intervention
21 mars 2010
-
Messages postés
125
Date d'inscription
vendredi 30 mai 2008
Statut
Membre
Dernière intervention
21 mars 2010
-
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?

4 réponses

Messages postés
40805
Date d'inscription
jeudi 28 août 2003
Statut
Modérateur
Dernière intervention
10 août 2020
4 887
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$

;-))
Messages postés
125
Date d'inscription
vendredi 30 mai 2008
Statut
Membre
Dernière intervention
21 mars 2010
18
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.
Messages postés
40805
Date d'inscription
jeudi 28 août 2003
Statut
Modérateur
Dernière intervention
10 août 2020
4 887
[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]$

;-))
Messages postés
125
Date d'inscription
vendredi 30 mai 2008
Statut
Membre
Dernière intervention
21 mars 2010
18
Tu es trop fort ;)
Messages postés
125
Date d'inscription
vendredi 30 mai 2008
Statut
Membre
Dernière intervention
21 mars 2010
18
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?
Messages postés
40805
Date d'inscription
jeudi 28 août 2003
Statut
Modérateur
Dernière intervention
10 août 2020
4 887
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 ;-))
Messages postés
40805
Date d'inscription
jeudi 28 août 2003
Statut
Modérateur
Dernière intervention
10 août 2020
4 887
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 ;-(
Messages postés
125
Date d'inscription
vendredi 30 mai 2008
Statut
Membre
Dernière intervention
21 mars 2010
18 >
Messages postés
40805
Date d'inscription
jeudi 28 août 2003
Statut
Modérateur
Dernière intervention
10 août 2020

Merci ;)