[BASH] Récupération et changement string

Récupération et changement string [BASH] - Shell/Batch - Programmation

Marsh Posté le 07-07-2014 à 15:51:38    

Bonjour à tous,
 
Je possède plusieurs fichiers XML. Dans chacun d'entre eux, j'ai quelque chose de ce type :
 
[...]
          <Attribute name="V_volume">-1252141245e-015</Attribute>
          <Attribute name="V_userZ">Default_User</Attribute>
          <Attribute name="majorip">SD25SDSFGH12DSFGH45</Attribute>
          <Attribute name="CESTEST">1215DSJFG5SJDF45REIUFTGOHK1558QSHGJKJLLLGE5R6TJ</Attribute>
        </RootAttributes>
        <Streams>
          <Stream format="1" role="">
          <Attribute name="majorid">D43DC61B00001BB4538EF3A300051112</Attribute>
          <Attribute name="My_user">Default_User</Attribute>
          <Attribute name="CESTEST">26XHCJ12SDH</Attribute>
[...]
 
Ce que j'ai besoin de faire est :
1/ Trouver toutes les lignes qui sont de type <Attribute name="CESTEST">**CONTENU**</Attribute>
2/ Tronquer la valeur **CONTENU** à 18 caractères
3/ Enregistrer l'output
 
Concrètement, je cherche à avoir l'output suivant :
          <Attribute name="V_volume">-1252141245e-015</Attribute>
          <Attribute name="V_userZ">Default_User</Attribute>
          <Attribute name="majorip">SD25SDSFGH12DSFGH45</Attribute>
         <Attribute name="CESTEST">1215DSJFG5SJDF45RE</Attribute>
       </RootAttributes>
        <Streams>
          <Stream format="1" role="">
          <Attribute name="majorid">D43DC61B00001BB4538EF3A300051112</Attribute>
          <Attribute name="My_user">Default_User</Attribute>
          <Attribute name="CESTEST">26XHCJ12SDH</Attribute>
 
J'ai testé avec sed et awk mais le résultat n'est pas probant car j'arrive à obtenir le 1215DSJFG5SJDF45RE mais les balises ont disparues avec :/
 
Merci pour votre aide

Reply

Marsh Posté le 07-07-2014 à 15:51:38   

Reply

Marsh Posté le 07-07-2014 à 21:03:14    

salut,
 

Code :
  1. awk '/<Attribute name="CESTEST">/{split($0,a,"[><]" );$0=a[1]"<"a[2]">"substr(a[3],1,18)"<"a[4]">"}1' fichier.xml

mais bon, si tu dois traiter souvent des fichiers xml, il vaut mieux investir ton temps dans l'apprentissage d'XSLT, xsltproc, ou xmlstarlet.

Reply

Marsh Posté le 07-07-2014 à 21:41:12    

Sous unix (ça peut aussi marcher sous windows, mais en prenant certaines précautions dues aux interprétations des quotes par le shell):
perl -pe 's|(?<=\Q<Attribute name="CESTEST">\E)(.{0,18}+)(.*)(?=\Q</Attribute>\E)|$1|' monfichier.xml
 
A la base c'est un substitute s/// mais je remplace le / habituel par un |, d’où un s||| à cause de la présence d'un / dans la balise fermante.
On cherche un groupe de entre 0 et 18 caractères, de manière greedy, suivi éventuellement d'autres caractères (.{0,18}+)(.*), groupe qu'on va garder
Avec un contexte <Attribute name="CESTEST"> à gauche (?<=\Q<Attribute name="CESTEST">\E)  (le \Q...\E sert a bloquer l'interprétation de caractères comme ayant un sens d'opérateur de la regexp)
et avec un contexte </Attribute> à droite (?=\Q</Attribute>\E)
 
La forme plus conventionnelle (et sans les \Q...\E en fait inutiles sauf pour la lisibilité ici) s/(?<=<Attribute name="CESTEST"> )(.{0,18}+)(.*)(?=<\/Attribute> )/$1/ marche aussi, bien sur, mais est nettement moins lisible.
 
A+,


Message édité par gilou le 07-07-2014 à 21:53:50

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 08-07-2014 à 09:49:47    


 
Merci BEAUCOUP pour votre aide.
Ca fonctionne sur plein de fichiers SAUF (et je viens de découvrir le cas) sur des fichiers xml où les balises se suivent... On parle par exemple de fichier xml de 200ko où toutes les balises sont stockées sur... une seule ligne... Avec ta formule sur ce type de fichier, il va traiter la première balise et s'arrêter. J'aurai besoin que le script poursuive la recherche.
 
Je précise que je ne suis pas auteur de ces fichiers :)

Reply

Marsh Posté le 08-07-2014 à 09:59:07    

Pour info, une version fonctionnelle :)
 
cat fichier.xml |\
awk '{
                n=match($0,"\"CESTEST\"" )
                if( n==0)
                {
                                print $0
                }
                else
                {
                                m=index(substr($0,n),">" )
                                o=index(substr($0,m+n),"<" )
                                if(o < 18 )
                                {
                                                print $0
                                }
                                else
                                {
                                                print substr($0,1,m+n+17) substr($0,m+n+o-1)
                                }
                }
}'
 
Merci encore pour votre aide

Reply

Marsh Posté le 08-07-2014 à 11:19:56    

LeBidule75 a écrit :


 
Merci BEAUCOUP pour votre aide.
Ca fonctionne sur plein de fichiers SAUF (et je viens de découvrir le cas) sur des fichiers xml où les balises se suivent... On parle par exemple de fichier xml de 200ko où toutes les balises sont stockées sur... une seule ligne... Avec ta formule sur ce type de fichier, il va traiter la première balise et s'arrêter. J'aurai besoin que le script poursuive la recherche.
 
Je précise que je ne suis pas auteur de ces fichiers :)

Voila pourquoi il faut préciser les choses dans son post initial.
 
Si tu peux avoir plusieurs tags <Attribute...> sur une même ligne, alors il suffit de faire:
perl -pe 's|(?<=\Q<Attribute name="CESTEST">\E)([^<]{0,18}+)([^<]*)(?=\Q</Attribute>\E)|$1|g' monfichier.xml  
le g final c'est pour faire une substitution incrémentale sur la ligne, et remplacer . par [^<] (tout caractère sauf < ) évitera d'être trop greedy et de faire la troncature entre le premier tag ouvrant <Attribute name="CESTEST"> et le dernier tag fermant </Attribute> de la ligne lorsqu'il y a plus d'un tag Attribute par ligne.
Et en dernier, après tests, si tout est ok et qu'on ne veut plus de fichier de backup ajouter l'option -i
perl -i -pe 's|(?<=\Q<Attribute name="CESTEST">\E)([^<]{0,18}+)([^<]*)(?=\Q</Attribute>\E)|$1|g' monfichier.xml  
 
 
Le seul cas de figure restant ou ça pourrait ne pas marcher, c'est si tu as des tags coupés en deux par un retour de ligne, auquel cas il vaut mieux lire tout le fichier d'un coup et non en mode ligne a ligne
Il y a un trick standard pour cela: le flag -0777  
Et pour un match multiline, utiliser l'option de regexp smg et non plus seulement g.
perl -0777 -pe 's|(?<=\Q<Attribute name="CESTEST">\E)([^<]{0,18}+)([^<]*)(?=\Q</Attribute>\E)|$1|smg' monfichier.xml  
Mais je doute que tu aies ceci dans ton fichier source, sinon, cela mérite en fait un traitement plus complexe, au cas ou un saut de ligne intervient dans un tag, etc.
 
A+,

Reply

Marsh Posté le 08-07-2014 à 12:41:07    

Citation :

Ca fonctionne sur plein de fichiers SAUF (et je viens de découvrir le cas) sur des fichiers xml où les balises se suivent...


une bonne raison supplémentaire pour privilégier XSLT pour le traitement de fichiers XML !

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed