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 .

156
demandé sur Mike 2013-03-17 18:16:47

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 avec libxml2 , xpath1 (cochez mon wrapper pour avoir des lignes délimitées sortie
  • xpath : installé via le module de perl XML::XPath , xpath1
  • xml_grep : installé via module de perl XML::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

.

205
répondu Gilles Quenot 2018-03-25 17:16:31

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'))"
15
répondu clacke 2013-03-18 07:53:00

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.

15
répondu BeniBela 2016-10-15 19:38:01

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
10
répondu Michael Kay 2013-03-17 16:55:38

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:

9
répondu Mike 2015-07-25 14:20:50

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

open 1.xml ;
ls //element/@id ;
for //p[@class="first"] echo text() ;
5
répondu choroba 2015-02-26 09:50:43

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'))
4
répondu sideshowbarker 2017-05-23 12:34:41

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).

2
répondu G. Cito 2014-03-05 16:37:31

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.

2
répondu pdr 2015-03-20 23:53:24

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 .

2
répondu Geoff Nixon 2015-12-23 07:29:57

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()")
1
répondu ccpizza 2018-02-05 00:37:56

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)

1
répondu mgrandi 2018-04-20 00:06:40

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.

0
répondu diemo 2018-05-17 11:24:55