Après avoir utiisé jq
pour manipuler du JSON en ligne de commande afin de construire une carte OpenStreetMap des câbles sous-marins d'Internet (voir mon turoriel de prise en main de jq), j'ai dû manipuler du XML en ligne de commande. Notamment, j'avais besoin d'utiliser le langage de requêtage XPath.
Le logiciel xmllint
(paquet Debian libxml2-utils) convient à mon besoin. Après coup, un collègue m'a conseillé xmlstarlet
(paquet Debian xmlstarlet). La sortie de xmlstarlet
est clé en main pour un usage traitement automatique de données, voir la fin de ce shaarli.
Regardons un cas d'usage concret.
Je veux analyser la sortie du sous-module stat du module rtmp pour nginx (paquet Debian libnginx-mod-rtmp dans les backports). Pour activer ce sous-module, il faut ajouter les lignes suivantes dans l'hôte virtuel désiré :
location /stat {
rtmp_stat all;
allow 127.0.0.1;
deny all;
}
Le XML renvoyé par le module RTMP a cette structure :
<?xml version="1.0" encoding="utf-8" ?>
<rtmp>
[…]
<server>
<application>
<name>live</name>
<live>
<stream>
<name>test</name>
[…]
</stream>
<stream>
<name>test2</name>
[…]
</stream>
<nclients>4</nclients>
</live>
</application>
<application>
<name>show</name>
<live>
<stream>
<name>test_mid</name>
[…]
</stream>
<stream>
<name>test_high</name>
[…]
</stream>
<stream>
<name>test2_mid</name>
[…]
</stream>
<stream>
<name>test2_high</name>
[…]
</stream>
<nclients>4</nclients>
</live>
</application>
</server>
</rtmp>
Je veux récupérer le nom de tous les flux reçus à l'instant T (en vrai, il s'agit de points de terminaison actifs). Comme les différents débits (medium, high, etc.) d'un même flux ne m'intéressent pas, je me concentre sur le premier bloc « application ».
La requête XPATH est donc celle-ci : « //server/application[1]/live/stream/name ».
Exécuter la requête XPath avec xmllint
: wget -q -O - https://monserveurnginx/stat | xmllint --xpath "//server/application[1]/live/stream/name" -
(le « - » final demande à xmllint
de lire l'entrée standard).
Exécuter la requête XPath avec xmlstarlet
: wget -q -O - https://monserveurnginx/stat | xmlstarlet sel -t -v '//server/application[1]/live/stream/name'
.
Je vois une différence entre xmllint et xmlstarlet : le deuxième donne directement la valeur de plusieurs éléments ayant le même nom, une valeur par ligne, donc c'est simple d'itérer dessus.
Avec xmllint
, on peut utiliser la fonction XPath « text() », mais alors le nom de plusieurs flux sont chaînés sans espacement, ce qui rend impossible un traitement humain ou automatisé. La fonction XPath « string() » affiche uniquement la valeur du premier élément trouvé. Bref, j'ai rien trouvé de satisfaisant avec xmllint
.