Éditer des fichiers XML en ligne de commande : xmlstarlet
Présentation
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
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