web-dev-qa-db-ger.com

Wie man XPath-Einzeiler von der Shell aus ausführt?

Gibt es ein Paket für Ubuntu und/oder CentOS, das ein Befehlszeilentool enthält, das einen XPath-Einzeiler wie foo //[email protected] filename.xml oder foo //[email protected] < filename.xml ausführen und die Ergebnisse Zeile für Zeile zurückgeben kann?

Ich bin auf der Suche nach etwas, das mir erlaubt, nur apt-get install foo oder yum install foo zu verwenden, und dann funktioniert es sofort, es sind keine Wrapper oder andere Anpassungen erforderlich.

Hier einige Beispiele von Dingen, die nahe kommen:

Nokogiri Wenn ich diesen Wrapper schreibe, könnte ich den Wrapper auf die oben beschriebene Weise aufrufen:

#!/usr/bin/Ruby

require 'nokogiri'

Nokogiri::XML(STDIN).xpath(ARGV[0]).each do |row|
  puts row
end

XML :: XPath. Würde mit diesem Wrapper funktionieren:

#!/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 aus XML :: XPath liefert zu viel Rauschen, -- NODE -- und attribute = "value".

xml_grep aus XML :: Twig kann keine Ausdrücke verarbeiten, die keine Elemente zurückgeben. Daher können Attributwerte nicht ohne weitere Verarbeitung extrahiert werden.

BEARBEITEN:

echo cat //element/@attribute | xmllint --Shell filename.xml liefert ein ähnliches Rauschen wie xpath.

xmllint --xpath //element/@attribute filename.xml gibt attribute = "value" zurück.

xmllint --xpath 'string(//element/@attribute)' filename.xml gibt zurück, was ich will, aber nur für den ersten Treffer.

Für eine andere Lösung, die die Frage beinahe erfüllt, gibt es hier ein XSLT, mit dem beliebige XPath-Ausdrücke ausgewertet werden können (erfordert Dyn: Evaluierungsunterstützung im XSLT-Prozessor):

<?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>

Führen Sie mit xsltproc --stringparam pattern //element/@attribute --stringparam value . arbitrary-xpath.xslt filename.xml aus.

166
clacke

Sie sollten diese Tools ausprobieren:

  • xmlstarlet: kann bearbeiten, auswählen, transformieren ... Standardmäßig nicht installiert, xpath1
  • xmllint: wird häufig standardmäßig mit libxml2, xpath1 installiert (aktivieren Sie mein wrapper , um die Ausgabe durch Zeilenumbrüche zu begrenzen
  • xpath: installiert über das Perl-Modul XML::XPath, xpath1
  • xml_grep: wird über das Perl-Modul XML::Twig, xpath1 (eingeschränkte Xpath-Nutzung) installiert
  • xidel: xpath3
  • saxon-lint: mein eigenes Projekt, Wrapper über die Saxon-HE-Java-Bibliothek von @Michael Kay, xpath3

xmllint wird mit libxml2-utils geliefert (kann als interaktive Shell mit dem --Shell-Schalter verwendet werden)

xmlstarlet ist xmlstarlet.

xpath wird mit Perls Modul XML::Xpath ausgeliefert

xml_grep wird mit Perls Modul XML::Twig geliefert.

xidel ist xidel

saxon-lint mit SaxonHE 9.6 , XPath 3.x (+ Retro-Kompatibilität)

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

Sie können auch meine Xidel ausprobieren. Es befindet sich nicht in einem Paket im Repository, aber Sie können es einfach von der Webseite herunterladen (es hat keine Abhängigkeiten).

Es hat eine einfache Syntax für diese Aufgabe:

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

Und es ist eines der wenigen dieser Tools, das XPath 2 unterstützt.

17
BeniBela

Ein Paket, das sehr wahrscheinlich bereits auf einem System installiert wird, ist python-lxml. Wenn ja, ist dies ohne Installation eines zusätzlichen Pakets möglich:

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

Saxon macht dies nicht nur für XPath 2.0, sondern auch für XQuery 1.0 und (in der kommerziellen Version) 3.0. Es kommt nicht als Linux-Paket, sondern als JAR-Datei. Syntax (die Sie leicht in ein einfaches Skript einwickeln können) ist

Java net.sf.saxon.Query -s:source.xml -qs://element/attribute
10
Michael Kay

Bei meiner Suche nach maven pom.xml-Dateien habe ich diese Frage ausgeführt. Ich hatte jedoch die folgenden Einschränkungen: 

  • muss plattformübergreifend laufen. 
  • muss auf allen großen Linux-Distributionen ohne zusätzliche Modulinstallation vorhanden sein
  • muss mit komplexen xml-Dateien wie maven pom.xml-Dateien umgehen können
  • einfache Syntax

Ich habe viele davon ohne Erfolg ausprobiert:

  • python lxml.etree ist nicht Teil der Standard-Python-Distribution
  • xml.etree ist aber mit komplexen maven pom.xml-Dateien nicht gut umzugehen, hat nicht tief genug gegraben
  • python xml.etree verarbeitet aus unbekannten Gründen keine maven pom.xml-Dateien
  • xmllint funktioniert auch nicht, Core-Dumps häufig auf Ubuntu 12.04 "xmllint: Verwendung von libxml Version 20708"

Die einzige Lösung, die ich gefunden habe, ist stabil, kurz und funktioniert auf vielen Plattformen, und sie ist ausgereift, ist die in Ruby integrierte rexml-Bibliothek:

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

Was mich dazu inspiriert hat, waren folgende Artikel: 

9
Mike

Vielleicht interessieren Sie sich auch für xsh . Es verfügt über einen interaktiven Modus, in dem Sie mit dem Dokument alles tun können, was Sie möchten:

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

Antwort von clacke ist großartig, aber ich denke, es funktioniert nur, wenn Ihre Quelle aus wohlgeformtem XML und nicht aus normalem HTML besteht.

So machen Sie dasselbe für normale Webinhalte: HTML-Dokumente, die nicht notwendigerweise wohlgeformtes XML sind:

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'))"

Verwenden Sie stattdessen html5lib (um sicherzustellen, dass Sie dasselbe Parsing-Verhalten wie Webbrowser erhalten. Da html5lib wie Browser-Parser den Parser-Anforderungen in der HTML-Spezifikation entspricht).

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

Zusätzlich zu XML ​​:: XSH und XML ​​:: XSH2 gibt es einige grep-ähnliche Hilfsprogramme wie App::xml_grep2 und XML::Twig (die xml_grep statt xml_grep2 enthalten). Dies kann sehr nützlich sein, wenn Sie an großen oder zahlreichen XML-Dateien für schnelle Oneliner oder Makefile-Ziele arbeiten. XML::Twig ist besonders nett, wenn Sie für einen Perl-Skriptansatz arbeiten möchten, wenn Sie ein wenig mehr Verarbeitungsaufwand wünschen als Ihr $Shell- und xmllintxstlproc-Angebot.

Das Nummerierungsschema in den Anwendungsnamen gibt an, dass es sich bei den "2" -Versionen um neuere/spätere Versionen von im Wesentlichen demselben Tool handelt, für die möglicherweise spätere Versionen anderer Module (oder von Perl selbst) erforderlich sind.

2
G. Cito

Ähnlich wie Mikes und Clackes Antworten gibt es hier den Python-Einzeiler (mit Python> = 2.5), um die Build-Version aus einer pom.xml-Datei abzurufen, die die Tatsache umgeht, dass pom.xml-Dateien normalerweise keine Dateisysteme haben Standard-Namespace, erscheinen Sie also in libxml nicht wohlgeformt:

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

Getestet unter Mac und Linux und erfordert keine zusätzlichen Pakete zur Installation.

2
pdr

Es ist zu erwähnen, dass nokogiri selbst ein Befehlszeilentool enthält, das mit gem install nokogiri installiert werden sollte.

Dieser Blogbeitrag könnte nützlich sein

2
Geoff Nixon

Mein Python Skript xgrep.py macht genau das. Um nach allen Attributen attribute von Elementen element in Dateien filename.xml ... zu suchen, würden Sie es wie folgt ausführen:

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

Es gibt verschiedene Schalter zur Steuerung der Ausgabe, z. B. -c zum Zählen von Übereinstimmungen, -i zum Einrücken der übereinstimmenden Teile und -l nur zum Ausgeben von Dateinamen.

Das Skript ist nicht als Debian- oder Ubuntu-Paket verfügbar, aber alle Abhängigkeiten sind vorhanden.

1
Andreas Nolda

Ich habe ein paar Befehlszeilen-XPath-Hilfsprogramme ausprobiert, und als ich merkte, dass ich zu viel Zeit damit verbringe, zu googeln und herauszufinden, wie sie funktionieren, schrieb ich den einfachsten XPath-Parser in Python, der das tat, was ich brauchte.

Das folgende Skript zeigt den Zeichenfolgewert, wenn der XPath-Ausdruck zu einer Zeichenfolge ausgewertet wird, oder zeigt den gesamten XML-Unterknoten, wenn das Ergebnis ein Knoten ist:

#!/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))

Es verwendet lxml - einen schnellen, in C geschriebenen XML-Parser, der nicht in der Standard-Python-Bibliothek enthalten ist. Installieren Sie es mit pip install lxml. Unter Linux/OSX muss möglicherweise Sudo vorangestellt werden.

Verwendungszweck:

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

lxml kann auch eine URL als Eingabe akzeptieren:

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

Extrahieren Sie das URL-Attribut unter einem Enclosure-Knoten, d. H. <Enclosure url="http:...""..>):

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

Xpath in Google Chrome

Nicht nebenstehende Anmerkung: Wenn Sie zufällig einen XPath-Ausdruck gegen das Markup einer Webseite ausführen möchten, können Sie dies direkt von Chrome devtools aus tun: Klicken Sie mit der rechten Maustaste auf die Seite in Chrome> wählen Sie Inspect und dann in den DevTools Console Fügen Sie Ihren XPath-Ausdruck als $x("//spam/eggs") ein.

Holen Sie sich alle Autoren auf dieser Seite:

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

Da dieses Projekt anscheinend ziemlich neu ist, checken Sie aus https://github.com/jeffbr13/xq , es scheint ein Wrapper um lxml zu sein, aber das ist alles, was Sie wirklich brauchen (und Ad-hoc-Lösungen mit Lxml in auch andere Antworten)

1
mgrandi

Hier ist ein xmlstarlet-Anwendungsfall zum Extrahieren von Daten aus geschachtelten Elementen elem1, elem2 in eine Textzeile aus diesem XML-Typ (der auch zeigt, wie Namespaces behandelt werden):

<?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>

Die Ausgabe wird sein

0.586 10.586 cue-in outro

In diesem Snippet entspricht -m dem verschachtelten Element elem2, -v gibt Attributwerte aus (mit Ausdrücken und relativer Adressierung), -o wörtlicher Text, -n fügt eine neue Zeile hinzu:

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

Wenn mehr Attribute von elem1 benötigt werden, kann man dies folgendermaßen tun (auch die concat () - Funktion wird angezeigt):

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

Beachten Sie die (IMO unnötige) Komplikation mit Namespaces (ns, die mit -N deklariert wurden), bei der ich fast auf xpath und xmlstarlet verzichten und einen schnellen Ad-hoc-Konverter schreiben musste.

0
diemo