Comment exécuter les one-liners XPath à partir du shell?

Existe-t-il un package, pour Ubuntu et / ou CentOS, qui dispose d’un outil de ligne de commande capable d’exécuter un one-liner XPath comme foo //element@atsortingbute filename.xml ou foo //element@atsortingbute < filename.xml et renvoyer les résultats ligne par ligne?

Je suis à la recherche de quelque chose qui me permettrait de simplement apt-get install foo ou yum install foo , puis de travailler immédiatement, sans encapsulation ou autre adaptation nécessaire.

Voici quelques exemples de choses qui se rapprochent:

Nokogiri. Si j’écris ce wrapper, je pourrais appeler le wrapper de la manière décrite ci-dessus:

 #!/usr/bin/ruby require 'nokogiri' Nokogiri::XML(STDIN).xpath(ARGV[0]).each do |row| puts row end 

XML :: XPath. Fonctionnerait avec ce wrapper:

 #!/usr/bin/perl use ssortingct; use warnings; use XML::XPath; my $root = XML::XPath->new(ioref => 'STDIN'); for my $node ($root->find($ARGV[0])->get_nodelist) { print($node->getData, "\n"); } 

xpath de XML :: XPath renvoie trop de bruit, -- NODE -- et atsortingbute = "value" .

xml_grep from XML :: Twig ne peut pas gérer les expressions qui ne renvoient pas d’éléments, et ne peuvent donc pas être utilisées pour extraire des valeurs d’atsortingbut sans traitement supplémentaire.

MODIFIER:

echo cat //element/@atsortingbute | xmllint --shell filename.xml echo cat //element/@atsortingbute | xmllint --shell filename.xml renvoie un bruit similaire à xpath .

xmllint --xpath //element/@atsortingbute filename.xml retourne atsortingbute = "value" .

xmllint --xpath 'ssortingng(//element/@atsortingbute)' filename.xml renvoie ce que je veux, mais uniquement pour la première correspondance.

Pour une autre solution presque satisfaisante pour la question, voici un XSLT qui peut être utilisé pour évaluer des expressions XPath arbitraires (requirejs dyn: évaluer le support dans le processeur XSLT):

           

Exécutez avec xsltproc --ssortingngparam pattern //element/@atsortingbute --ssortingngparam value . arbitrary-xpath.xslt filename.xml xsltproc --ssortingngparam pattern //element/@atsortingbute --ssortingngparam value . arbitrary-xpath.xslt filename.xml .

Vous devriez essayer ces outils:

  • xmlstarlet : peut éditer, sélectionner, transformer … Non installé par défaut, xpath1
  • xmllint : souvent installé par défaut avec libxml2 , xpath1 (vérifiez que mon wrapper a des sorties délimitées par des nouvelles lignes)
  • xpath : installé via le module XML::XPath perl XML::XPath , xpath1
  • xml_grep : installé via le module XML::Twig perl XML::Twig , xpath1 (utilisation limitée de xpath)
  • xidel : xpath3
  • saxon-lint : mon propre projet, enveloppe la bibliothèque Java Saxon-HE de @Michael Kay, xpath3

xmllint est fourni avec libxml2-utils (peut être utilisé comme shell interactif avec le commutateur --shell )

xmlstarlet est xmlstarlet .

xpath est fourni avec le module XML::Xpath perl XML::Xpath

xml_grep est livré avec le module XML::Twig perl XML::Twig

xidel est xidel

saxon-lint utilisant SaxonHE 9.6 , XPath 3.x (+ compatibilité rétro)

Ex:

 xmllint --xpath '//element/@atsortingbute' file.xml xmlstarlet sel -t -v "//element/@atsortingbute" file.xml xpath -q -e '//element/@atsortingbute' file.xml xidel -se '//element/@atsortingbute' file.xml saxon-lint --xpath '//element/@atsortingbute' file.xml 
  • page xmlstarlet
  • homme xmllint
  • page xpath
  • xml_grep
  • xidel
  • saxon-lint

.

python-lxml est un paquet très susceptible d’être installé sur un système. Si c’est le cas, cela est possible sans installer de paquet supplémentaire:

 python -c "from lxml.etree import parse; from sys import stdin; print '\n'.join(parse(stdin).xpath('//element/@atsortingbute'))" 

Vous pouvez aussi essayer mon Xidel . Il ne se trouve pas dans un package du référentiel, mais vous pouvez simplement le télécharger depuis la page Web (il ne comporte aucune dépendance).

Il a une syntaxe simple pour cette tâche:

 xidel filename.xml -e '//element/@atsortingbute' 

Et c’est l’un des rares de ces outils qui supporte XPath 2.

Saxon le fera non seulement pour XPath 2.0, mais aussi pour XQuery 1.0 et (dans la version commerciale) 3.0. Il ne s’agit pas d’un package Linux, mais d’un fichier jar. La syntaxe (que vous pouvez facilement envelopper dans un simple script) est

 java net.sf.saxon.Query -s:source.xml -qs://element/atsortingbute 

Dans ma recherche pour interroger les fichiers maven pom.xml, j’ai rencontré cette question. Cependant, j’avais les limitations suivantes:

  • doit exécuter multi-plateforme.
  • doit exister sur toutes les principales dissortingbutions Linux sans aucune installation de module supplémentaire
  • doit gérer les fichiers xml complexes tels que les fichiers maven pom.xml
  • syntaxe simple

J’ai essayé plusieurs des solutions ci-dessus sans succès:

  • python lxml.etree ne fait pas partie de la dissortingbution standard de python
  • xml.etree est mais ne gère pas bien les fichiers maven pom.xml complexes, n’a pas creusé assez profondément
  • python xml.etree ne gère pas les fichiers maven pom.xml pour une raison inconnue
  • xmllint ne fonctionne pas non plus, le kernel se vide souvent sur Ubuntu 12.04 “xmllint: en utilisant libxml version 20708”

La seule solution que j’ai trouvée qui soit stable, courte et fonctionne sur de nombreuses plates-formes et qui est mature est la bibliothèque intégrée rexml de ruby:

 ruby -r rexml/document -e 'include REXML; p XPath.first(Document.new($stdin), "/project/version/text()")' < pom.xml 

Ce qui m'a inspiré pour trouver celui-ci, ce sont les articles suivants:

  • Didacticiel Ruby / XML, XSLT et XPath
  • IBM: Ruby on Rails et XML

Vous pourriez également être intéressé par xsh . Il comporte un mode interactif où vous pouvez faire ce que vous voulez avec le document:

 open 1.xml ; ls //element/@id ; for //p[@class="first"] echo text() ; 

La réponse de clacke est géniale, mais je pense que cela ne fonctionne que si votre source est du XML bien formé, pas du HTML normal.

Donc, pour faire la même chose pour le contenu Web normal – les documents HTML qui ne sont pas nécessairement des fichiers XML bien formés:

 echo "

foo

bar

baz" | python -c "from sys import stdin; \ from lxml import html; \ print '\n'.join(html.tossortingng(node) for node in html.parse(stdin).xpath('//p'))"

Et à la place, utilisez html5lib (pour vous assurer d’avoir le même comportement d’parsing que les navigateurs Web, car, comme les parsingurs de navigateur, html5lib est conforme aux exigences d’parsing dans la spécification HTML).

 echo "

foo

bar

baz" | python -c "from sys import stdin; \ import html5lib; from lxml import html; \ doc = html5lib.parse(stdin, treebuilder='lxml', namespaceHTMLElements=False); \ print '\n'.join(html.tossortingng(node) for node in doc.xpath('//p'))

En plus de XML :: XSH et XML :: XSH2, il existe des utilitaires similaires à grep comme App::xml_grep2 et XML::Twig (qui inclut xml_grep plutôt que xml_grep2 ). Celles-ci peuvent être très utiles lorsque vous travaillez sur un grand nombre de fichiers XML ou sur de nombreux oneliners rapides ou des cibles Makefile . XML::Twig est particulièrement xmllint pour une approche de script perl lorsque vous souhaitez un traitement un peu plus important que votre $SHELL et xmllint xstlproc .

Le schéma de numérotation dans les noms d’application indique que les versions “2” sont des versions plus récentes ou plus récentes du même outil, ce qui peut nécessiter des versions ultérieures d’autres modules (ou de perl lui-même).

Semblable aux réponses de Mike et de clacke, voici le one-liner de python (utilisant python> = 2.5) pour obtenir la version de compilation à partir d’un fichier pom.xml contournant le fait que les fichiers pom.xml n’ont normalement pas de dtd ou espace de noms par défaut, donc ne semble pas bien formé à libxml:

 python -c "import xml.etree.ElementTree as ET; \ print(ET.parse(open('pom.xml')).getroot().find('\ {http://maven.apache.org/POM/4.0.0}version').text)" 

Testé sur Mac et Linux, ne nécessite aucun paquet supplémentaire pour être installé.

Il convient de mentionner que nokogiri lui-même est livré avec un outil de ligne de commande, qui devrait être installé avec gem install nokogiri .

Vous pourriez trouver ce post de blog utile .

J’ai essayé quelques utilitaires XPath en ligne de commande et quand j’ai réalisé que je passais trop de temps à googler et à comprendre comment ils fonctionnaient, j’ai donc écrit l’parsingur XPath le plus simple possible en Python.

Le script ci-dessous montre la valeur de la chaîne si l’expression XPath évalue une chaîne ou affiche l’intégralité du sous-noeud XML si le résultat est un noeud:

 #!/usr/bin/env python import sys from lxml import etree tree = etree.parse(sys.argv[1]) xpath = sys.argv[2] for e in tree.xpath(xpath): if isinstance(e, str): print(e) else: print((e.text and e.text.ssortingp()) or etree.tossortingng(e)) 

Il utilise lxml – un parsingur XML rapide écrit en C qui n’est pas inclus dans la bibliothèque python standard. Installez-le avec pip install lxml . Sous Linux / OSX, il peut être nécessaire de préfixer sudo .

Usage:

 python xmlcat.py file.xml "//mynode" 

lxml peut également accepter une URL en entrée:

 python xmlcat.py http://example.com/file.xml "//mynode" 

Extrayez l’atsortingbut url sous un noeud de boîtier, à savoir ) :

 python xmlcat.py xmlcat.py file.xml "//enclosure/@url" 

Xpath dans Google Chrome

Remarque: Si par hasard vous souhaitez exécuter une expression XPath sur le balisage d’une page Web, vous pouvez le faire directement depuis Chrome devtools: cliquez avec le bouton droit de la souris sur Chrome> sélectionnez Inspecter, puis dans les DevTools. console collez votre expression XPath sous la forme $x("//spam/eggs") .

Obtenez tous les auteurs sur cette page:

 $x("//*[@class='user-details']/a/text()") 

Étant donné que ce projet est apparemment assez récent, consultez https://github.com/jeffbr13/xq , semble être un wrapper autour de lxml , mais c’est tout ce dont vous avez vraiment besoin (et des solutions ad hoc utilisant lxml dans d’autres réponses également) )

Voici un cas d’utilisation de xmlstarlet pour extraire des données d’éléments nesteds elem1, elem2 sur une ligne de texte de ce type de XML (montrant également comment gérer les espaces de noms):

       

La sortie sera

 0.586 10.586 cue-in outro 

Dans cet extrait de code, -m correspond aux valeurs d’atsortingbuts nestedes elem2, -v (avec expressions et adressage relatif), -o texte littéral, -n ajoute une nouvelle ligne:

 xml sel -N ns="http://xml-namespace-uri" -t -m '//ns:elem1/ns:elem2' \ -v ../@time -o " " -v '../@time + ../@length' -o " " -v @value -o " " -v @type -n file.xml 

Si plus d’atsortingbuts d’elem1 sont nécessaires, on peut le faire comme ça (montrant également la fonction concat ()):

 xml sel -N ns="http://xml-namespace-uri" -t -m '//ns:elem1/ns:elem2/..' \ -v 'concat(@time, " ", @time + @length, " ", ns:elem2/@value, " ", ns:elem2/@type)' -n file.xml 

Notez la complication (inutile de l’IMO) avec les espaces de noms (ns, déclarés avec -N), qui me laissait presque tomber sur xpath et xmlstarlet, et d’écrire un convertisseur ad hoc rapide.