219 votes

Comment exécuter des phrases d'une ligne XPath à partir du shell ?

Existe-t-il un paquetage, pour Ubuntu et/ou CentOS, qui dispose d'un outil en ligne de commande capable d'exécuter une ligne de commande XPath du type foo //element@attribute filename.xml o foo //element@attribute < filename.xml et renvoyer les résultats ligne par ligne ?

Je cherche quelque chose qui me permettrait de juste apt-get install foo o yum install foo et fonctionne alors tout simplement, sans wrapper ou autre adaptation nécessaire.

Voici quelques exemples de choses qui s'en 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. Cela fonctionnerait 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 de XML::XPath renvoie trop de bruit, -- NODE -- et attribute = "value" .

xml_grep de 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 les valeurs des attributs sans traitement supplémentaire.

EDITAR:

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 répondant presque à la question, voici un XSLT qui peut être utilisé pour évaluer des expressions XPath arbitraires (nécessite le support de dyn:evaluate 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 .

0 votes

+1 pour la bonne question et pour le brainstorming sur la recherche d'un moyen simple et fiable d'imprimer plusieurs résultats, chacun sur une nouvelle ligne.

1 votes

Notez que le "bruit" de xpath est sur STDERR et non STDOUT.

0 votes

@miken32 Non. Je voulais seulement la valeur pour la sortie. hastebin.com/ekarexumeg.bash

314voto

sputnick Points 31663

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-utils , xpath1 (vérifiez mon emballage d'avoir --xpath activer les très anciennes versions et les sorties délimitées par des lignes nouvelles (v < 2.9.9)
  • 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, wrapper sur la bibliothèque Java Saxon-HE de @Michael Kay, xpath3

xmllint est livré avec libxml2-utils (peut être utilisé comme shell interactif avec l'option --shell interrupteur)

xmlstarlet es xmlstarlet .

xpath est fourni avec le module perl XML::Xpath

xml_grep est fourni avec le module perl XML::Twig

xidel es xidel

saxon-lint en 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

.

8 votes

Excellent ! xmlstarlet sel -T -t -m '//element/@attribute' -v '.' -n filename.xml fait exactement ce que je veux !

2 votes

Remarque : la rumeur voulait que xmlstarlet soit abandonné, mais il est à nouveau en développement actif.

6 votes

Remarque : Certaines anciennes versions de xmllint ne supporte pas les arguments de ligne de commande --xpath mais la plupart semblent soutenir --shell . Une sortie un peu plus sale, mais toujours utile en cas de problème.

26voto

BeniBela Points 6863

Vous pouvez également essayer mon Xidel . Il n'est pas dans un paquet dans le dépôt, mais vous pouvez simplement le télécharger depuis la page web (il n'a pas de dépendances).

Il dispose d'une 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.

5 votes

Xidel a l'air plutôt cool, mais vous devriez probablement mentionner que vous êtes également l'auteur de cet outil que vous recommandez.

1 votes

Saxon et saxon-lint utilisent xpath3 ;)

0 votes

Xidel (0..8.win32.zip) apparaît comme ayant un malware sur Virustotal. Essayez donc à vos risques et périls virustotal.com/#/file/

17voto

clacke Points 1534

Un paquet qui a de fortes chances d'être déjà installé sur un système est le suivant 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')))"

1 votes

Comment passer le nom du fichier ?

5 votes

Cela fonctionne sur stdin . Il n'est donc pas nécessaire d'inclure open() et close() dans une phrase déjà assez longue. Pour analyser un fichier, il suffit de lancer python -c "from lxml.etree import parse; from sys import stdin; print '\n'.join(parse(stdin).xpath('//element/@attribute'))" < my_file.xml et laissez votre shell s'occuper de la recherche, de l'ouverture et de la fermeture des fichiers.

10voto

Michael Kay Points 52194

Saxon le fera non seulement pour XPath 2.0, mais aussi pour XQuery 1.0 et (dans la version commerciale) 3.0. Il n'est pas livré sous forme de package Linux, mais sous forme de fichier jar. La syntaxe (que vous pouvez facilement envelopper dans un simple script) est la suivante

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

MISE À JOUR 2020

Saxon 10.0 comprend l'outil Gizmo, qui peut être utilisé de manière interactive ou par lots à partir de la ligne de commande. Par exemple

java net.sf.saxon.Gizmo -s:source.xml
/>show //element/@attribute
/>quit

0 votes

SaxonB est sous Ubuntu, paquetage libsaxonb-java mais si j'exécute saxonb-xquery -qs://element/@attribute -s:filename.xml Je reçois SENR0001: Cannot serialize a free-standing attribute node Même problème que pour l'exemple suivant xml_grep .

3 votes

Si vous voulez voir tous les détails du nœud d'attribut sélectionné par cette requête, utilisez l'option -wrap sur la ligne de commande. Si vous voulez juste la valeur de la chaîne de l'attribut, ajoutez /string() à la requête.

0 votes

Merci. L'ajout de /string() est plus proche. Mais cela produit un en-tête XML et met tous les résultats sur une seule ligne, donc toujours pas de résultat.

10voto

Mike Points 75

Dans mes recherches pour interroger les fichiers pom.xml de maven, j'ai rencontré cette question. Cependant, j'avais les limitations suivantes :

  • doit fonctionner sur plusieurs plates-formes.
  • doit exister sur toutes les principales distributions linux sans 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 maven pom.xml complexes, je n'ai 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 noyau se vide souvent sur ubuntu 12.04 "xmllint : using libxml version 20708".

La solution que j'ai trouvée, qui est stable, courte, fonctionne sur de nombreuses plateformes et est mature, est la librairie rexml intégrée à ruby :

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

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

1 votes

C'est un critère encore plus étroit que la question, donc cela convient parfaitement comme réponse. Je suis sûr que de nombreuses personnes qui se sont trouvées dans votre situation seront aidées par vos recherches. Je garde xmlstarlet comme la réponse acceptée, parce qu'elle correspond à mes critères généraux et qu'elle est vraiment chouette . Mais j'aurai probablement besoin de votre solution de temps en temps.

2 votes

J'ajouterais qu'à éviter les guillemets autour du résultat utiliser puts au lieu de p dans la commande Ruby.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X