Wie führe ich XPath-Einzeiler aus Shell aus?

Gibt es ein Paket für Ubuntu und / oder CentOS, das über ein Befehlszeilentool verfügt, das einen XPath-Einzeiler wie das foo //element@attribute filename.xml oder das foo //element@attribute < filename.xml ausführen kann und die Ergebnisse Zeile für Zeile zurückgeben?

Ich bin auf der Suche nach etwas, das mir erlauben würde, nur apt-get install foo oder yum install foo und dann funktioniert out-of-the-box, keine Wrapper oder andere Anpassung notwendig.

Hier sind einige Beispiele für Dinge, die nahekommen:

Nokogiri. Wenn ich diesen Wrapper schreibe, könnte ich den Wrapper wie oben beschrieben 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 arbeiten:

 #!/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 von XML :: XPath gibt zu viel Rauschen zurück, -- NODE -- und attribute = "value" .

xml_grep aus XML :: Twig kann keine Ausdrücke verarbeiten, die keine Elemente zurückgeben. Sie können daher nicht verwendet werden, um Attributwerte ohne weitere Verarbeitung zu extrahieren.

BEARBEITEN:

echo cat //element/@attribute | xmllint --shell filename.xml echo cat //element/@attribute | xmllint --shell filename.xml gibt ein ähnliches Rauschen zurück wie xpath .

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

xmllint --xpath 'string(//element/@attribute)' filename.xml zurück, was ich will, aber nur für die erste Übereinstimmung.

Für eine andere Lösung, die die Frage fast erfüllt, ist hier ein XSLT, mit dem beliebige XPath-Ausdrücke ausgewertet werden können (erfordert dyn: evaluate support im XSLT-processor):

           

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

Sie sollten diese Werkzeuge ausprobieren:

  • xmlstarlet : kann bearbeiten, auswählen, transformieren … Standardmäßig nicht installiert, xpath1
  • xmllint : wird oft standardmäßig mit libxml2 , xpath1 installiert (überprüfen Sie, ob mein Wrapper Zeilentrennzeichen mit Trennzeichen enthält)
  • xpath : installiert über das Perl-Modul XML::XPath , xpath1
  • xml_grep : installiert über das Perl-Modul XML::Twig , xpath1 (eingeschränkte xpath-Nutzung)
  • xidel : xpath3
  • saxon-lint : mein eigenes Projekt, Wrapper über Michael Kay’s Saxon-HE Java Bibliothek, xpath3

xmllint kommt mit libxml2-utils (kann als interaktive Shell mit dem --shell Schalter verwendet werden)

xmlstarlet ist xmlstarlet .

xpath kommt mit xpath Modul XML::Xpath

xml_grep kommt mit xml_grep Modul XML::Twig

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 
  • xmlstarlet Seite
  • Mann xmllint
  • XPath Seite
  • xml_grep
  • xidel
  • Saxon-Lint

.

Ein Paket, das sehr wahrscheinlich bereits auf einem System installiert wird, ist python-lxml . Wenn dies der Fall ist, 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'))" 

Sie können auch meine Xidel ausprobieren . Es ist 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.

Saxon wird dies nicht nur für XPath 2.0 tun, 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 einfach in ein einfaches Skript einfügen können) ist

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

Bei meiner Suche nach maven pom.xml Dateien lief ich über diese Frage. Allerdings hatte ich folgende Einschränkungen:

  • muss plattformübergreifend laufen.
  • muss auf allen gängigen Linux-Distributionen ohne zusätzliche Modulinstallation vorhanden sein
  • muss komplexe XML-Dateien wie maven pom.xml-Dateien behandeln
  • einfache Syntax

Ich habe viele der oben genannten ohne Erfolg versucht:

  • python lxml.etree ist nicht Teil der Standard-Python-Distribution
  • xml.etree ist aber nicht mit komplexen Maven pom.xml Dateien gut, habe nicht tief genug gegraben
  • python xml.etree behandelt maven pom.xml Dateien aus unbekannten Gründen nicht
  • Xmllint funktioniert auch nicht, Core-Dumps oft auf Ubuntu 12.04 “xmllint: mit libxml Version 20708”

Die einzige Lösung, die ich gefunden habe, ist stabil, kurz und funktioniert auf vielen Plattformen und das ist ausgereift, die rexml lib, die in Ruby eingebaut ist:

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

Was mich dazu inspirierte, diesen zu finden, waren die folgenden Artikel:

  • Ruby / XML-, XSLT- und XPath-Tutorial
  • IBM: Ruby on Rails und XML

Das könnte Sie auch interessieren. Es verfügt über einen interaktiven Modus, in dem Sie mit dem Dokument tun können, was Sie wollen:

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

Clackes Antwort ist großartig, aber ich denke, dass es nur funktioniert, wenn Ihre Quelle wohlgeformtes XML ist, kein normales HTML.

So machen Sie das gleiche für normale Webinhalte – HTML-Dokumente, die nicht unbedingt wohlgeformtes XML sind:

 echo "

foo

bar

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

Und stattdessen html5lib verwenden (um sicherzustellen, dass Sie das gleiche Analyseverhalten wie Web-Browser erhalten – weil html5lib wie die Browser-Parser den Parser-Anforderungen in der HTML-Spezifikation entspricht).

 echo "

foo

bar

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

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 ). Diese können sehr nützlich sein, wenn Sie an einer großen oder zahlreichen XML-Dateien für schnelle Oneliner oder Makefile Ziele arbeiten. XML::Twig eignet sich besonders gut für perl Scripting-Ansätze, wenn Sie ein wenig mehr Verarbeitung als Ihr $SHELL und xstlproc Angebot xstlproc .

Das Nummerierungsschema in den Anwendungsnamen zeigt an, dass die “2” -Versionen eine neuere / spätere Version von im Wesentlichen demselben Werkzeug sind, das möglicherweise spätere Versionen anderer Module (oder von perl selbst) erfordert.

Ähnlich wie Mikes und Clackes Antworten, hier ist der Python-Einzeiler (mit Python> = 2.5), um die Build-Version aus einer pom.xml-Datei zu erhalten, die umgeht, dass pom.xml-Dateien normalerweise kein dtd oder haben Standard-Namespace, daher erscheint libxml nicht gut formatiert:

 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 auf Mac und Linux, und erfordert keine zusätzlichen Pakete installiert werden.

Es muss erwähnt werden, dass Nokogiri selbst mit einem Kommandozeilen-Tool ausgeliefert wird, das mit dem gem install nokogiri .

Sie könnten diesen Blogeintrag nützlich finden .

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

Das folgende Skript zeigt den Zeichenfolgenwert, wenn der XPath-Ausdruck zu einem String ausgewertet wird, oder 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 XML-Parser, der in C geschrieben ist und nicht in der Standard-Python-Bibliothek enthalten ist. Installiere es mit pip install lxml . Unter Linux / OSX muss möglicherweise sudo vorangestellt werden.

Verwendung:

 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, z. B. ) :

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

Xpath in Google Chrome

Als weitere Randnotiz: Wenn Sie zufällig einen XPath-Ausdruck gegen das Markup einer Webseite ausführen möchten, können Sie dies direkt über die Chrome-Devices tun: Klicken Sie in Chrome mit der rechten Maustaste auf die Seite und wählen Sie Inspect und dann DevTools console Fügen Sie Ihren XPath-Ausdruck als $x("//spam/eggs") .

Erhalten Sie alle Autoren auf dieser Seite:

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

Da dieses Projekt anscheinend ziemlich neu ist, sehen Sie sich https://github.com/jeffbr13/xq an , es scheint ein Wrapper um lxml , aber das ist alles, was Sie wirklich brauchen (und veröffentlichte Ad-hoc-Lösungen mit lxml auch in anderen Antworten) )

Hier ist ein xmlstarlet-Anwendungsfall, um Daten aus verschachtelten Elementen elem1, elem2 in eine Textzeile aus diesem XML-Typ zu extrahieren (zeigt auch, wie Namespaces behandelt werden):

 < ?xml version="1.0" encoding="UTF-8" standalone="yes" ?>      

Die Ausgabe wird sein

 0.586 10.586 cue-in outro 

In diesem Codeausschnitt stimmt -m mit den verschachtelten elem2-Werten überein, -v gibt Attributwerte aus (mit Ausdrücken und relativer Adressierung), -o Literaltext, -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 es so machen (zeigt auch die function 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 

Beachten Sie die (IMO unnötige) Komplikation mit Namespaces (ns, deklariert mit -N), die mich fast auf Xpath und xmlstarlet aufgeben und einen schnellen Ad-hoc-Konverter schreiben.