Éditer des fichiers XML en ligne de commande : xmlstarlet

Présentation

XML!!!!

De nos jours, nous sommes cernés de fichiers XML. Ils sont présents partout que ce soit pour les préférences des applications (gconf pour Gnome) les fichiers en rapport avec les GPS (GPX, KML), les descriptions d'interface graphiques (Qt UI, GTK Glade, Mozilla XUL), les documents (OpenOffice ODF Microsoft OOXML) etc... La liste serait sans fin.

Je ne rentrerai pas ici dans les considérations polémiques sur le bien fondé de ces choix, mais les faits sont la : nous devons faire avec. Il est relativement facile d'écrire une application capable de travailler avec ces fichiers, les langages de haut niveau proposent tous des API dédiées plus ou moins simples. Même en C la libxml2 rend cela relativement facile.

Mais la complication vient lorsque l'on veux travailler en shell. La ou des fichiers à plat étaient faciles à utiliser avec sed/grep/awk et quelques regex bien senties, le XML est une horreur sans nom. Sortir le Python ou le Perl n'est pas toujours possible (disponibilité sur la plateforme, versions datant de Mathusalem ...). Dans ces moments, on se rabat souvent sur bash[1]. Et c'est la que xmlstarlet va vous venir en aide. Il s'agit d'un programme en ligne de commande qui va se charger du parsing XML et vous permettre de lire et modifier facilement[2] vos fichiers.

Installation

xmlstarlet devrait être disponible dans toutes les bonnes crémeries[3].

Par exemple pour ArchLinux, il est dans AUR

yaourt -S xmlstarlet

Ou bien pour debian et dérivées

aptitude install xmlstarlet

Sinon, il est fourni dans une version portable compilée statiquement avec la libxml2 et la libxslt. Téléchargez le rpm xmlstarlet-1.0.1-1.i586.rpm sur SourceForge. Il suffit alors de le décompresser dans un répertoire temporaire avec la commande[4] :

rpm2cpio xmlstarlet-1.0.1-1.i586.rpm | cpio -idmv

et de récupérer le binaire présent dans ./usr/bin/xml

Utilisation

hello

Nous allons nous contenter de lire et de modifier des valeurs et des attributs. Pour une utilisation plus avancée, allez lire la documentation.

Comme exemple, nous allons utiliser un fichier UI de Qt très simple. Une boîte de dialogue avec un bouton sans layout ni rien.

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Dialog</class>
 <widget class="QDialog" name="Dialog">
  <property name="windowTitle">
   <string>Dialog</string>
  </property>
  <widget class="QPushButton" name="pushButton">
   <property name="text">
    <string>Hello</string>
   </property>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>

Lecture

Commençons par regarder la structure :

$ xml el hello.ui
ui
ui/class
ui/widget
ui/widget/property
ui/widget/property/string
ui/widget/widget
ui/widget/widget/property
ui/widget/widget/property/string
ui/resources
ui/connections

Nous retrouvons ici tous les champs de notre fichier XML. Ce sont ces chaines de caractères qui vont nous permettre d'accéder aux valeurs à lire et modifier.

On peut aussi regarder les paramètres '-a' et '-v' qui donnent plus d'infos sur la structure. (attributs et valeurs). Par exemple pour le bouton :

$ xml el -v hello.ui
...
ui/widget/widget[@class='QPushButton' and @name='pushButton']
ui/widget/widget/property[@name='text']
ui/widget/widget/property/string

Maintenant, si nous voulons voir le texte du bouton, on va utiliser la commande 'sel' :

$ xml sel -t -v "/ui/widget/widget/property/string" hello.ui
Hello
  • sel: pour selectionner un chemin (xpath)
  • -t: requête dans le document
  • -v: on veux la valeur
  • /ui/widget/widget/property/string : c'est le chemin que nous cherchons.

Alors bien sur ici, nous n'avons qu'un seul bouton. Mais il faut pouvoir caractériser plus finement a chaine recherchée. C'est à dire la property text du widget dont le nom est pushButton. Cela donne donc :

$ xml sel -t -v "/ui/widget/widget[@name='pushButton']/property[@name='text']/string" hello.ui
Hello
  • widget[@name='pushButton'] nous permet de dire que l'on cherche le widget dont l'attribut name est pushButton
  • idem pour property[@name='text']

On peut aussi chercher les valeurs des attributs. Quelle est la classe de ma fenêtre principale ?

$ xml sel -t -v "/ui/widget/@class" hello.ui
QDialog
  • @class: nous cherchons l'attribut class de widget

Modification

Maintenant, nous allons utiliser la commande 'ed' pour modifier notre fichier XML.

$ xml ed -u "/ui/widget/widget[@name='pushButton']/property[@name='text']/string" -v GoodBye hello.ui
...
      <property name="text">
        <string>GoodBye</string>
      </property>
...
  • -u: update
  • -v: nouvelle valeur

De même pour changer un attribut, par exemple, le nom de notre bouton :

$ xml ed -u "/ui/widget/widget[@name='pushButton']/@name" -v btn hello.ui
...
    <widget class="QPushButton" name="btn">

Voyez aussi les options :

  • -i: pour insérer un élément
  • -d: pour effacer un élément
  • ...

Voila, avec ces quelques commandes simple, on peut déjà sortir quelque chose de ces fichiers XML à partir d'un petit scrip shell. xmlstarlet permet d'en faire beaucoup plus, je vous renvoi donc une nouvelle fois vers la documentation

Notes

[1] ou pire csh :(

[2] enfin, relativement, on parle quand même d'XML la !

[3] votre gestionnaire de paquet favoris

[4] Sous ArchLinux, il existe un script rpmextract.sh qui fait le boulot

Vus : 5319
Publié par JJL : 28