Forum général.général Shell script / parse XML, limites ?

Posté par . Licence CC by-sa
Tags :
3
28
nov.
2013

Bonjour,
Je suis dans une impasse, je dois parser un fichier typique au contenu ci-dessous pour le transformer en CSV en en conservant uniquement certaines parties… j'ai environ 10000 lignes !!! à parser tous les mois :/

Ci-dessous une démo du fichier XML:
http://pastebin.fr/31819

Idéallement je dois avoir en output (ex):

192.168.1.2;22344;3;Mine
192.168.1.2;22301;4;Nope
192.168.1.2;22899;2;Goog
etc… et ce pour chaque Host…

J'ai bien fait des tentatives avec des boucles while + grep -A$x (x est incrémenté jusqu a ce qu il trouve la balise de fin </Host et </Report à chaque fois) mais clairement c'est imbittable d'autant que le fichier devant être lu il faut avancer deans sans retraiter les lignes précédentes (j'ai tenté de numéroter les lignes pour les supprimer avec sed etc… mais vraiment ca ne fonctionne pas:(

(PS: en dessus et au dessous il y a d autre balises XML inutiles…)

Je me demande donc si quelqu'un aurait une idée en shell script qui satisferait ce besoin ?

Par avance merci !!!

  • # perl /python

    Posté par (page perso) . Évalué à 5.

    avec un "vrai" langage de programmation (perl, python …), c'est relativement facile : à vu de nez, moins de 20 lignes de code …

  • # Plus simple qu'un script

    Posté par . Évalué à 2.

    Tout est dans le titre: une transformation XSLT sur une structure XML aussi simple que celle-ci ne devrait pas poser de soucis.
    Ensuite xsltproc et c'est fini.

  • # perdu...

    Posté par . Évalué à 0.

    Je ne code pas en perl ni en python :(

    quant à xsltproc ok mais je ne comprend pas bien comment cela fonctionne il faut faire du xml2xslt ou quoi je comprend pas trop…

    d autant qu il y a plein d autre champ xml avant et apres qui ne m interesse pas donc je ne sais pas si c est possible.

    Si tu peux m en dire plus ?

    • [^] # Re: perdu...

      Posté par (page perso) . Évalué à 1.

      quant à xsltproc ok mais je ne comprend pas bien comment cela fonctionne il faut faire du xml2xslt ou quoi je comprend pas trop…

      Non non, XSLT est un langage qui permet de transformer un arbre XML en un autre arbre XML ou tout autre sortie texte.

      Ça vaut le coup de creuser cette proposition en plus une recherche donne rapidement des pistes (sur stackoverflow notamment)

      Benoît

  • # xmlgrep ?

    Posté par . Évalué à 3.

    sinon une piste :

    1. grep -v de ce que tu ne veux pas (ici plugin, text, factor) renvoyer dans un fichier temporaire
    2. suppression de retour à la ligne (peut se grouper avec 1) avec des pipes.
    3. reinjecter des retours uniquement là ou il faut (apres un /report, ou un /host
    4. remplacer les balises par des ;
    • [^] # Re: xmlgrep ?

      Posté par . Évalué à 0.

      pour le coup ca merite d etre testé!

      merci

  • # xslt -> vraiment compliqué

    Posté par . Évalué à 2.

    pour le coup j ai regardé du coté de xslt ca me semble VRAIMENT compliqué…

    • [^] # Re: xslt -> vraiment compliqué

      Posté par . Évalué à 4.

      probablement pas plus que d'ecrire un gros script qui parse ton xml

    • [^] # Re: xslt -> vraiment compliqué

      Posté par (page perso) . Évalué à 3.

      xslt c'est le mal absolu, mais pour parser du xml, ça reste pratique. Pour éviter xslt, je crois qu'il n'y a pas d'autres choix que d'éviter xml…

      Ce script xslt fait le travail. Il s'utilise avec xsltproc (généralement installé par défaut) ainsi:
      xsltproc script.xslt fichier.xml

      <?xml version="1.0" encoding="UTF-8"?>
      <xsl:stylesheet version="1.0"
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      
      
      <xsl:output method="text"
        encoding="UTF-8"
        indent="no"/>
      
      <xsl:strip-space elements="*"/>
      
      <xsl:template match="title">
        
        <xsl:value-of select="../../@name"/>
        <xsl:text>;</xsl:text>
        
        <xsl:value-of select="../@ID"/>
        <xsl:text>;</xsl:text>
        
        <xsl:value-of select="../@niveau"/>
        <xsl:text>;</xsl:text>
        
        <xsl:apply-templates/>
        
        <xsl:text>&#xa;</xsl:text>
      </xsl:template>
      
      
      
      <xsl:template match="plugin"/>
      <xsl:template match="text"/>
      <xsl:template match="factor"/>
      
      </xsl:stylesheet>
        <xsl:value-of select="../../@name"/>
        <xsl:text>;</xsl:text>
        
        <xsl:value-of select="../@ID"/>
        <xsl:text>;</xsl:text>
        
        <xsl:value-of select="../@niveau"/>
        <xsl:text>;</xsl:text>
        
        <xsl:apply-templates/>
        
        <xsl:text>&#xa;</xsl:text>
      </xsl:template>
      
      
      
      <xsl:template match="plugin"/>
      <xsl:template match="text"/>
      <xsl:template match="factor"/>
      
      </xsl:stylesheet>
      

      PS: Visiblement, linuxfr n'aime pas xml non plus… Il faut couper au premier /xsl:stylesheet, et peut-être qu'il est possible de retrouver les commentaires dans les sources de la page web…

      • [^] # Re: xslt -> vraiment compliqué

        Posté par (page perso) . Évalué à 2.

        Ah, et j'oubliais: il faut rajouter un nœud racine au fichier, comme dit ci-dessous.

        • [^] # Re: xslt -> vraiment compliqué

          Posté par . Évalué à 0.

          Elo,
          Ca ne fonctionne pas j obtiens le résultat suivant:

          xsltproc script.xslt d2
          script.xslt:35: parser error : Extra content at the end of the document

          ^
          cannot parse script.xslt

          merci enormément quoi qu il en soit!!!

          Je vais me contenter du script perl qui fait le job a merveil!

          • [^] # Re: xslt -> vraiment compliqué

            Posté par (page perso) . Évalué à 2.

            C'est parce que le moteur de linuxfr ne parvient pas à afficher correctement le script xslt, et duplique une partie du script. Comme indiqué ci-dessus « il faut couper au premier /xsl:stylesheet », c'est à dire supprimer tout ce qui n'est pas coloré par le moteur de linuxfr, de sorte que le script ne comporte que 34 lignes.

            Je viens de ré-essayer, ça marche très bien.

  • # Un petit script perl

    Posté par (page perso) . Évalué à 2.

    Avec XML::LibXML :

    #!/usr/bin/perl 
    
    use strict;
    use warnings;
    
    use XML::LibXML;
    
    my $parser = XML::LibXML->new;
    
    my $struct = $parser->parse_file('data.xml');
    
    foreach my $host ($struct->findnodes('//Host')) {
        my ($name) = grep { $_->getName() eq 'name' } $host->getAttributes();
        foreach my $report ($host->findnodes('Report')) {
            my $id = $report->getAttribute('ID');
            my $niveau = $report->getAttribute('niveau');
            my ($title) = $report->findnodes('title');
            print $name->getValue(), ';', $id, ';', $niveau, ";", $title->to_literal, "\n" ;
        }
    }

    PS : c'est sans doute pas optimal, c'est la première fois que je parse du xml sérieusement et pas à coups de regexps ;)

    • [^] # Re: Un petit script perl

      Posté par (page perso) . Évalué à 1. Dernière modification le 28/11/13 à 21:58.

      D'ailleurs

      my ($name) = grep { $_->getName() eq 'name' } $host->getAttributes();

      peut aussi s'écrire

      my $name = $host->getAttribute('name')

      mais quand j'ai écrit la première ligne, je ne connaissais pas encore l'autre méthode :)

      Et il faudrait bien sûr vérifier à chaque fois que les attributs existent bien toussa.

      • [^] # Re: Un petit script perl

        Posté par . Évalué à 0.

        merci beaucoup!

        mais ca ne fonctionne pas:

        ./pe.pl
        d2:12: parser error : Extra content at the end of the document
        Host name="192.168.1.4">

        peut etre manque t il une boucle ?

        • [^] # Re: Un petit script perl

          Posté par (page perso) . Évalué à 2.

          C'est parce que le fichier xml donné en exemple est mal construit: il n'a pas de nœud racine, de sorte que le parseur y trouve deux nœuds racines (les deux <Host name="...">). Il faut y rajouter un nœud racine qui encadre tout le fichier (du genre <xml>...</xml>).

    • [^] # Re: Un petit script perl

      Posté par . Évalué à 0.

      1000 * 1000 merci! ca marche au top

  • # Merci Merci et encore Merci à tous

    Posté par . Évalué à 1. Dernière modification le 29/11/13 à 08:20.

    Vous m'avez sauvés :D
    j'en étais là : !!!!!
    pour le coup je crois que je vais vraiment me mettre au perl!

    #!/bin/bash
    
    
    check=1
    
    while read a
    do
            NUM=1
            grep -e "<Host name=" > /dev/null 2>&1
    if [ $? = 0 ]
    then
                    while [ $check != "0" ]
            do
                    grep -A"${NUM}" "$a" d2 | grep "</Host"  > /dev/null 2>&1
                    if [ $? = 0 ]
                    then
                            check=0
                            IPS=$(echo $a | awk -F"\"" '{print $2}')
                            grep -A"${NUM}" "$a" d2 | grep -v Host | tr "\n" " " | sed "s/<\/Report>/;/g" | sed -e "s/<Report niveau=//g" -e "s/ID=//g" -e "s/title>//g" | tr ";" "\n" | awk '{print $1";"$2";"$3}' | sed "s/\"//g" | sed "s/<\///g" | sed -e "s/<//g" -e "s/>//g" |sed "s/;;//g" | sed "/^$/d" | grep -e ";3;" -e ";4"
                    else
                            NUM=$(expr $NUM + 1)
                    fi
            done
    
    
    fi
    done
  • # awk

    Posté par (page perso) . Évalué à 2.

    Je parse souvent du XML avec awk, alors oui c'est crade mais c'est hyper rapide, exemple

    awk -F"<|>|=" '{
      if ($0 ~ /Host name/) {
        host=$3
      } else if ($0 ~ /ID/) {
        linehost=host";"$4
      } else if ($0 ~ /title/) {
        linehost=linehost";"$3
        print linehost
        linehost=""
      }
    }' toto.xml
    
    root@game:~# sh toto.sh
    "192.168.1.2";"22344";Mine
    "192.168.1.2";"22301";Nope
    "192.168.1.2";"22899";Goog
    "192.168.1.4";"22111";M
    "192.168.1.4";"22864";Nope
    "192.168.1.4";"22800";Goog
    

    Is it a Bird? Is it a Plane?? No, it's Super Poil !!!

  • # elinks

    Posté par . Évalué à 3.

    Une autre idée pour transformer le fichier en texte:

    elinks -dump tongrosfichier.xml

  • # Allez vive le dredi !

    Posté par . Évalué à 4.

    Bon c'est dredi on peu avoiner un peu :)

    Perl … c'est pour les amateurs de soirées cuirs et chaînes (et je cite Larry Wall :) ) dans six mois tu seras incapable de comprendre ton script.
    XSLT … c'est pour ceux qui ont des cases en trop ou en moins, difficile a déterminer, faudrait en parler dans un forum :)

    Ensuite je dirais bien fait, le XML c'est quand même pas super top dans certain cas de figures, généralement 1.bit.able, surtout quand il y a plus de contenant que de contenu.
    mais bon vu c'est un petit peu utilisé :) … passons.

    Et dans ces cas désespéré je te conseille : xml2obj une recette python
    pour transformer du XML ( :=* ) en objet python que tu peu parcourir et faire des trucs de codeurs avec.

    Python c'est beau, c'est bon, sans matière grasse et huile de palme, et il y a de gros morceaux dedans.

    A je tiens a vous rappeler qu'il est interdit de maltraiter des octets (fussent ils en utf-8) et que l'utilisation du XML est quand même un peu "BorderLine"

    sur ce Bon Dredi :)

    • [^] # Re: Allez vive le dredi !

      Posté par (page perso) . Évalué à 3. Dernière modification le 29/11/13 à 17:07.

      Python c'est beau, c'est bon, sans matière grasse et huile de palme, et il y a de gros morceaux dedans.

      J'utilise python pour sage (un logiciel pour faire des maths en python), et j'en ai toujours été content, et je le recommanderai à quiconque veuille faire de la théorie des groupes ou des nombres. J'utilise divers autres logiciels écrits en python (dont le système de ports de Gentoo), et j'en suis très content aussi. Et je trouve la philosophie de python respectable et intéressante, mais je ne vois pas en quoi elle devrait être universelle.

      J'ai jamais compris ce besoin de décrédibiliser d'autres langages comme perl (ou xslt, que je n'ai jamais utilisé, mais a priori me semble intéressant), comme s'ils représentaient un danger ou que sais-je. C'est d'ailleurs la seule chose qui me déplaît vraiment dans python : l'intolérence dont font preuve certains programmeurs à l'égard des autres langages, que l'on retrouve beaucoup moins ailleurs, et qui me fait un peu froid dans le dos.

      Parce que bon, c'est pas parce qu'on a été traumatisé un jour par un script perl-cgi écrit avec les pieds il y a quinze ans qu'il faut rejeter d'emblée tout un pan des programmeurs libristes, qui se seraient visiblement trompés de langage.

      Et perso, je trouve que pour quelqu'un qui a besoin d'un langage pour résoudre de petits problèmes, et n'a pas d'intérêt particulier pour la programmation et les gros programmes, perl est on ne peut plus adapté : le CPAN fournira tout ce qu'il faut pour éviter d'avoir à écrire un algorithme, et la culture par l'exemple à coup de recettes de cuisines de perl est très pratique : la doc de python est quand même beaucoup plus formelle (ce qui peut ou non être une bonne chose, suivant l'utilisateur et ses besoins).

      Edit: bon, si c'était un appel à troll, j'espère ne pas trop être tombé dedans ;)

      • [^] # Re: Allez vive le dredi !

        Posté par . Évalué à 1.

        Edit: bon, si c'était un appel à troll, j'espère ne pas trop être tombé dedans ;)

        Et bien si, et le troll était tellement gros et velus que je croyais que personne ne tomberais dedans :)

        il n'y a pas de mauvais langages … que de mauvais codeurs :)

        Et quand je dis des conneries aussi grosses … cela me repose l'esprit
        Et pi c'est dredi :)

        • [^] # Re: Allez vive le dredi !

          Posté par (page perso) . Évalué à 1.

          Et pi c'est dredi :)

          C'est bien vrai, mais c'est quand même un troll un peu trop répétitif et à sens unique, qui pourrait créer de mauvais préjugés pour ceux qui ne connaissent pas ces langages (et il y a un tas de gens qui n'ont jamais écrit une ligne de perl et en ont un mauvais préjugé, donc le troll marche), donc une réponse me semble quand même d'usage, et ne fait pas de mal en tous cas ;)

    • [^] # Re: Allez vive le dredi !

      Posté par . Évalué à 3.

      Python c'est beau, c'est bon, sans matière grasse et huile de palme, et il y a de gros morceaux dedans.

      ais arrêtez avec cette légende urbaine. Python, c'est moche, pas cohérent, et pousse à prendre de mauvaises habitudes, et empêche l'expressivité du programmeur.

      Ruby est bien mieux fichu que cette horreur.

      En plus un Python, c'est dangereux.

      • [^] # Re: Allez vive le dredi !

        Posté par . Évalué à 1.

        Anaseto … voila comment on alimente un troll du dredi, avec de la pure mauvaise foi, première pression à froid
        demoulée à chaud.

        En plus un Python, c'est dangereux.

        Au moins c'est pas un langage de … zut il faut plus dire tarlouze, gonzesse non plus, pas tapette, pédé encore moins …

        pffiooouuu c'est pas facile d'être bête et méchant de nos jours.

  • # xmlstarlet

    Posté par . Évalué à 5.

    Pour travailler avec des fichiers XML en shell script, ce que je connais de mieux c'est xmlstarlet.
    http://xmlstar.sourceforge.net/

    • [^] # Re: xmlstarlet

      Posté par . Évalué à 4. Dernière modification le 04/12/13 à 12:20.

      Je suis bien d'accord! Exemple, pour la transformation demandée

      $ xmlstarlet sel   -t -m "/*/Host" -m "Report" -v "../@name" -o ";" -v "@ID" -o ';' -v "@niveau" -o ";" -v "title" -n fichier.xml

      après avoir bien sûr ajouté un élément racine comme dit plus haut.

      Inutile de se mettre à perl (ouf!) et à peine à xslt :)

  • # du choix

    Posté par . Évalué à 1. Dernière modification le 03/12/13 à 23:29.

    Si c'est bien indenté, tu peux t'en sortir avec awk (mon préféré).
    Si c'est vraiment du xml, il y a une extension à awk, mentionnée plus haut.

    Sinon, il aura tout ce qu'il faut dans des langage plus généralistes comme python ou java (Stax).

    Par contre du shell et du grep à mon avis, c'est mort.

    Le fichier a l'air quand même conséquent, il vaut donc mieux privilégier les parseurs en flux (à base de expat (C) ou Stax (java)) et pas ceux qui chargent le document complet (xslt, libxml, lxml).

Suivre le flux des commentaires

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