Comment exécuter XPath one-liners à partir de shell?
y a-t-il un paquet là-bas, pour Ubuntu et/ou CentOS, qui dispose d'un outil en ligne de commande qui peut exécuter un XPath one-liner comme foo //element@attribute filename.xml
ou foo //element@attribute < filename.xml
et retourner les résultats ligne par ligne?
je suis à la recherche de quelque chose qui me permettrait de juste apt-get install foo
ou yum install foo
et puis fonctionne tout simplement hors-de-la-boîte, aucun emballage ou autre adaptation nécessaire.
voici quelques exemples de choses qui se rapprochent:
Nokogiri. Si j'écris cet emballage, je pourrais l'appeler 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.
#!/usr/bin/perl
use strict;
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
à partir de XML::XPath retourne trop de bruit, -- NODE --
et attribute = "value"
.
xml_grep
à partir de XML::Twig ne peut pas manipuler des expressions qui ne renvoient pas d'éléments, donc ne peut pas être utilisé pour extraire des valeurs d'attribut sans plus traitement.
EDIT:
echo cat //element/@attribute | xmllint --shell filename.xml
renvoie un bruit similaire à xpath
.
xmllint --xpath //element/@attribute filename.xml
renvoie attribute = "value"
.
xmllint --xpath 'string(//element/@attribute)' filename.xml
retourne ce que je veux, mais seulement pour le premier match.
pour une autre solution presque satisfaisante, voici un XSLT qui peut être utilisé pour évaluer des expressions XPath arbitraires (nécessite dyn: evaluate support in the XSLT de processeur):
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:dyn="http://exslt.org/dynamic" extension-element-prefixes="dyn">
<xsl:output omit-xml-declaration="yes" indent="no" method="text"/>
<xsl:template match="/">
<xsl:for-each select="dyn:evaluate($pattern)">
<xsl:value-of select="dyn:evaluate($value)"/>
<xsl:value-of select="'
'"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Courir avec xsltproc --stringparam pattern //element/@attribute --stringparam value . arbitrary-xpath.xslt filename.xml
.
13 réponses
vous devriez essayer ces outils:
-
xmlstarlet
: peut modifier, sélectionner, transformer... Non installé par défaut, xpath1 -
xmllint
: souvent installé par défaut aveclibxml2
, xpath1 (cochez mon wrapper pour avoir des lignes délimitées sortie -
xpath
: installé via le module de perlXML::XPath
, xpath1 -
xml_grep
: installé via module de perlXML::Twig
, xpath1 (usage limité de xpath) -
xidel
: xpath3 -
saxon-lint
: mon propre projet, papier d'emballage sur @Michael Kay Saxon-IL de bibliothèque Java, xpath3
xmllint
vient avec libxml2-utils
(peut être utilisé comme shell interactif avec le commutateur --shell
)
xmlstarlet
est xmlstarlet
.
xpath
vient avec perl's module XML::Xpath
xml_grep
est livré avec le module de perl XML::Twig
xidel
est xidel
saxon-lint
utilisant SaxonHE 9.6 , XPath 3.x (+compatibilité rétro)
Ex:
xmllint --xpath '//element/@attribute' file.xml
xmlstarlet sel -t -v "//element/@attribute" file.xml
xpath -q -e '//element/@attribute' file.xml
xidel -se '//element/@attribute' file.xml
saxon-lint --xpath '//element/@attribute' file.xml
.
Un paquet qui est très susceptible d'être installé sur un système déjà est python-lxml
. 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/@attribute'))"
vous pouvez également essayer mon Xidel . Il n'est pas dans un paquet dans le dépôt, mais vous pouvez le télécharger depuis la page web (il n'a pas de dépendances).
il a la syntaxe simple pour cette tâche:
xidel filename.xml -e '//element/@attribute'
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 paquet Linux, mais d'un fichier jar. La syntaxe (que vous pouvez facilement envelopper dans un script simple) est
java net.sf.saxon.Query -s:source.xml -qs://element/attribute
dans ma recherche pour interroger maven pom.fichiers xml j'ai parcouru cette question. Cependant, j'avais les limitations suivantes:
- doit s'exécuter de la croix-plate-forme.
- doit exister sur toutes les principales distributions linux sans aucune installation de module supplémentaire
- doit traiter des fichiers xml complexes tels que maven pom.fichiers xml
- syntaxe simple
j'ai essayé beaucoup de la ci-dessus sans succès:
- python lxml.etree ne fait pas partie de la distribution standard de python
- xml.etree est un Maven pom complexe, mais ne le manipule pas.fichiers xml bien, n'ont pas creusé assez profond
- Python xml.etree ne manipule pas maven pom.fichiers xml pour raison inconnue
- xmllint ne fonctionne pas non plus, core dumps souvent sur ubuntu 12.04 "xmllint: utiliser la version 20708 de libxml "
la seule solution que j'ai trouvée qui est stable, courte et qui fonctionne sur de nombreuses plateformes et qui est arrivée à maturité est l'intégration de lib de rexml dans 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 était les articles suivants:
clacke la réponse de est grande, mais je pense ne fonctionne que si votre source est un format XML bien formé, pas normal HTML.
donc faire la même chose pour le contenu Web normal-HTML docs qui ne sont pas nécessairement bien formés XML:
echo "<p>foo<div>bar</div><p>baz" | python -c "from sys import stdin; \
from lxml import html; \
print '\n'.join(html.tostring(node) for node in html.parse(stdin).xpath('//p'))"
et d'utiliser à la place html5lib (pour vous assurer que vous obtenez le même comportement d'analyse que les navigateurs Web-parce que comme les analyseurs de navigateur, html5lib se conforme aux exigences d'analyse dans la spécification HTML).
echo "<p>foo<div>bar</div><p>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.tostring(node) for node in doc.xpath('//p'))
en plus de XML:: XSH et XML:: XSH2 Il ya quelques grep
- comme les services publics suck comme App::xml_grep2
et XML::Twig
(qui comprend xml_grep
au lieu de xml_grep2
). Ceux-ci peuvent être très utiles pour travailler sur un grand ou de nombreux fichiers XML pour des onelineurs rapides ou des cibles Makefile
. XML::Twig
est particulièrement agréable à travailler avec pour un perl
script approche si vous souhaitez un peu plus de traitement que votre $SHELL
et xmllint
xstlproc
.
le schéma de numérotation dans les noms de demande indique que les" 2 "versions sont des versions plus récentes/plus récentes du même outil qui peut exiger des versions plus récentes d'autres modules (ou de perl
lui-même).
similaire aux réponses de Mike et clacke, voici le python one-liner (utilisant python >= 2.5) pour obtenir la version de construction à partir d'un pom.fichier xml qui contourne le fait que pom.les fichiers xml n'ont normalement pas de DTD ou d'espace de noms par défaut, donc ne semblent pas bien formés pour 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, et ne nécessite pas de paquets supplémentaires à installer.
il convient de mentionner que nokogiri lui-même navires avec un outil de ligne de commande, qui devrait être installé avec gem install nokogiri
.
vous pourriez trouver cet article 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 fonctionnent, j'ai donc écrit le plus simple possible XPath parser en Python qui a fait ce dont j'avais besoin.
le script ci-dessous montre la valeur de la chaîne si L'expression XPath est évaluée à une chaîne, ou montre le sous-code XML entier 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.strip()) or etree.tostring(e))
il utilise lxml
- un analyseur XML rapide écrit en C qui n'est pas inclus dans la bibliothèque standard de python. Installez-le avec pip install lxml
. Sur Linux / OSX pourrait avoir besoin d'un préfixe avec sudo
.
Utilisation:
python xmlcat.py file.xml "//mynode"
lxml peut également accepter une URL en entrée:
python xmlcat.py http://example.com/file.xml "//mynode"
extraire l'attribut url sous un noeud de boîtier, c'est-à-dire <enclosure url="http:...""..>)
:
python xmlcat.py xmlcat.py file.xml "//enclosure/@url"
Xpath dans Google Chrome
comme sans rapport note latérale: si par hasard vous voulez lancer une expression XPath contre le markup d'une page web, alors vous pouvez le faire directement depuis les devtools de Chrome: cliquez avec le bouton droit de la souris sur la page dans Chrome > select Inspect, puis dans la console de DevTools collez votre expression XPath comme $x("//spam/eggs")
.
obtenir tous les auteurs sur cette page:
$x("//*[@class='user-details']/a/text()")
puisque ce projet est apparemment assez nouveau, cochez https://github.com/jeffbr13/xq , semble être un papier d'emballage autour de lxml
, mais c'est tout ce dont vous avez vraiment besoin (et posté des solutions ad hoc utilisant lxml dans d'autres réponses ainsi)
voici un cas d'utilisation de xmlstarlet pour extraire des données à partir d'éléments imbriqués elem1, elem2 vers une ligne de texte à partir de ce TYPE DE XML (montrant aussi comment gérer les espaces de noms):
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<mydoctype xmlns="http://xml-namespace-uri" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xml-namespace-uri http://xsd-uri" format="20171221A" date="2018-05-15">
<elem1 time="0.586" length="10.586">
<elem2 value="cue-in" type="outro" />
</elem1>
</mydoctype>
la sortie sera
0.586 10.586 cue-in outro
dans cet extrait, -m correspond aux valeurs imbriquées de l'attribut elem2, -v outputs (avec expressions et adressage relatif), - o literal text, - 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'attributs sont nécessaires à partir de elem1, on peut le faire comme ceci (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 IMO) avec les espaces de noms (ns, déclaré avec-N), qui m'a fait presque abandonner sur xpath et xmlstarlet, et l'écriture d'un convertisseur ad-hoc rapide.