web-dev-qa-db-fra.com

Comment exécuter des one-liners XPath depuis un shell?

Existe-t-il un paquet, pour Ubuntu et/ou CentOS, qui possède un outil de ligne de commande pouvant exécuter une ligne unique XPath telle que foo //element@attribute filename.xml ou foo //element@attribute < filename.xml et renvoyer les résultats ligne par ligne?

Je cherche quelque chose qui me permettrait de simplement apt-get install foo ou yum install foo et que cela fonctionne ensuite immédiatement, sans emballage ni autre adaptation nécessaire.

Voici quelques exemples de choses qui se rapprochent:

Nokogiri. Si j'écris ce wrapper, 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. Travaillerait avec ce wrapper:

#!/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 from XML :: XPath renvoie trop de bruit, -- NODE -- et attribute = "value".

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

MODIFIER:

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 renvoie ce que je veux, mais uniquement pour le premier match.

Pour une autre solution répondant presque à la question, voici un XSLT qui peut être utilisé pour évaluer des expressions XPath arbitraires (requiert la prise en charge de dyn: evalu dans le processeur XSLT):

<?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="'&#10;'"/>
    </xsl:for-each> 
  </xsl:template>
</xsl:stylesheet>

Exécuter avec xsltproc --stringparam pattern //element/@attribute --stringparam value . arbitrary-xpath.xslt filename.xml.

166
clacke

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 mon wrapper pour avoir une sortie délimitée par des nouvelles lignes
  • xpath: installé via le module Perl XML::XPath, xpath1
  • xml_grep: installé via le module Perl XML::Twig, xpath1 (utilisation limitée de xpath)
  • xidel: xpath3
  • saxon-lint: mon propre projet, enveloppe sur la bibliothèque Java Saxon-HE de Michael Kay, xpath3

xmllint est livré avec libxml2-utils (peut être utilisé en tant que shell interactif avec le commutateur --Shell)

xmlstarlet est xmlstarlet.

xpath est livré avec le module Perl XML::Xpath

xml_grep est livré avec le module 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

.

223
Gilles Quenot

Vous pouvez aussi essayer mon Xidel . Il ne fait pas partie d'un paquet dans le référentiel, mais vous pouvez simplement le télécharger à partir de la page Web (il n'a pas de dépendances).

Il a une syntaxe simple pour cette tâche:

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

Et c'est l'un des rares outils prenant en charge XPath 2.

17
BeniBela

python-lxml est un paquet qui est très probablement 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/@attribute'))"
15
clacke

Saxon le fera non seulement pour XPath 2.0, mais également pour XQuery 1.0 et (dans la version commerciale) 3.0. Cela ne vient pas comme un paquet Linux, mais comme 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
Michael Kay

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

  • doit fonctionner multi-plateforme. 
  • doit exister sur toutes les distributions majeures de Linux sans aucune installation de module supplémentaire
  • doit gérer des 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 distribution standard de python
  • xml.etree est, mais ne gère pas bien, les fichiers complexes maven pom.xml, n’ont pas été suffisamment approfondis
  • python xml.etree ne gère pas les fichiers maven pom.xml pour une raison inconnue
  • xmllint ne fonctionne pas non plus, les core dumps sont souvent sous ubuntu 12.04 "xmllint: utilisation de la version 20708 de libxml"

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 librairie rexml intégrée 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
Mike

Vous pourriez aussi ê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() ;
5
choroba

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

Il en va de même pour le contenu Web normal - les documents HTML qui ne sont pas nécessairement bien formés en 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 plutôt html5lib (pour vous assurer d'obtenir le même comportement d'analyse que les navigateurs Web car, tout comme les analyseurs syntaxiques de navigateur, html5lib est conforme aux exigences d'analyse de 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
sideshowbarker

En plus de XML ​​:: XSH et XML ​​:: XSH2 , il existe des utilitaires de type grep- ressemblant à 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 fichier XML volumineux ou sur de nombreux fichiers XML pour obtenir des résultats rapides ou des cibles Makefile. XML::Twig est particulièrement agréable à utiliser avec une approche de script Perl lorsque vous souhaitez un peu plus de traitement que vos offres $Shell et xmllintxstlproc.

Le schéma de numérotation dans les noms d'applications indique que les versions "2" sont des versions plus récentes/ultérieures du même outil, ce qui peut nécessiter des versions ultérieures d'autres modules (ou de Perl elle-même).

2
G. Cito

Semblable aux réponses de Mike et de Clacke, voici la ligne de texte python one-liner (utilisant python> = 2.5) pour obtenir la version de construction à partir d’un fichier pom.xml qui évite le fait que les fichiers pom.xml n’ont normalement pas de dtd ou namespace par défaut, donc ne semblez pas bien formé 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 l'installation d'aucun paquet supplémentaire.

2
pdr

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

Vous pourriez trouver ce billet de blog utile

2
Geoff Nixon

Mon Python script xgrep.py fait exactement cela. Pour rechercher tous les attributs attribute des éléments element dans les fichiers filename.xml ..., vous devez l'exécuter comme suit:

xgrep.py "//element/@attribute" filename.xml ...

Il existe différents commutateurs pour contrôler la sortie, tels que -c pour compter les correspondances, -i pour indenter les parties correspondantes et -l pour ne fournir que les noms de fichiers.

Le script n'est pas disponible en tant que paquet Debian ou Ubuntu, mais toutes ses dépendances le sont.

1
Andreas Nolda

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'analyseur XPath le plus simple possible en Python, qui a fait ce dont j'avais besoin.

Le script ci-dessous montre la valeur de chaîne si l'expression XPath est évaluée à une chaîne ou affiche le sous-noeud XML complet si le résultat est un nœud:

#!/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 Python. 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 tant qu'entrée:

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

Extrayez l’attribut url sous un nœud de boîtier, c.-à-d. <Enclosure url="http:...""..>):

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

Xpath dans Google Chrome

Remarque secondaire non liée: Si, par hasard, vous souhaitez exécuter une expression XPath par rapport au balisage d’une page Web, vous pouvez le faire directement à partir de Chrome devtools: faites un clic droit sur la page dans Chrome> sélectionnez Inspecter, puis dans 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()")
1
ccpizza

Comme ce projet est apparemment relativement nouveau, consultez https://github.com/jeffbr13/xq , semble être un wrapper autour de lxml, mais c’est tout ce dont vous avez besoin d'autres réponses aussi)

1
mgrandi

Voici un cas d'utilisation xmlstarlet pour extraire des données d'éléments imbriqués elem1, elem2 sur une ligne de texte à partir de ce type de XML (montrant également 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 à l'elem2 imbriqué, -v fournit les valeurs d'attribut (avec les expressions et l'adressage relatif), -o le 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 vous avez besoin de plus d'attributs de elem1, vous pouvez 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 (OMI inutile) avec les espaces de noms (ns, déclarés avec -N), qui m'a fait presque renoncer à xpath et xmlstarlet et à l'écriture d'un convertisseur ad-hoc rapide.

0
diemo