Jak wykonać XPath one-liners z shella?

Czy istnieje pakiet, dla Ubuntu i / lub CentOS, który ma narzędzie wiersza poleceń, które może wykonać XPath jednowierszową jak foo //element@attribute filename.xml lub foo //element@attribute < filename.xml i zwracać wyniki linia po linii?

Szukam czegoś, co pozwoli mi po prostu apt-get install foo lub yum install foo i wtedy po prostu działa out-of-the-box, bez opakowań i innych niezbędnych adaptacji.

Oto kilka przykładów rzeczy, które zbliżają się:

Nokogiri. Jeśli napiszę ten wrapper, mogę nazwać wrapper w sposób opisane powyżej:

#!/usr/bin/ruby

require 'nokogiri'

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

XML:: XPath. Działa z tym opakowaniem:

#!/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 z metody XML:: XPath zwraca zbyt wiele szumów, -- NODE -- i attribute = "value".

xml_grep from XML:: Twig nie może obsługiwać wyrażeń, które nie zwracają elementów, więc nie może być używany do wyodrębniania wartości atrybutów bez dalszego przetwarzania.

EDIT:

echo cat //element/@attribute | xmllint --shell filename.xml zwraca szum podobny do xpath.

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

xmllint --xpath 'string(//element/@attribute)' filename.xml zwraca to, co chcę, ale tylko na pierwszy mecz.

Dla innego rozwiązania prawie spełniającego pytanie, tutaj jest XSLT, który może być używany do oceny dowolnych wyrażeń XPath (wymaga dyn: evaluate wsparcie w procesorze 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>

Run with xsltproc --stringparam pattern //element/@attribute --stringparam value . arbitrary-xpath.xslt filename.xml.

Author: Mike, 2013-03-17

13 answers

Powinieneś wypróbować te narzędzia:

  • xmlstarlet : może edytować, wybierać, przekształcać... Nie jest instalowany domyślnie, xpath1
  • xmllint: często instalowane domyślnie z libxml2, xpath1 (sprawdź mój wrapper , aby mieć rozdzielane nowe linie wyjścia
  • xpath : zainstalowany przez moduł Perla XML::XPath, xpath1
  • Po zainstalowaniu modułu Perla, xpath1 (ograniczone użycie xpath)
  • xidel: xpath3
  • saxon-lint: mój własny projekt, wrapper over @ Michael Kay ' s Saxon-HE Java library, xpath3

xmllint zawiera libxml2-utils (może być używany jako interaktywna powłoka z przełącznikiem --shell)

xmlstarlet is xmlstarlet.

xpath pochodzi z modułu PerlaXML::Xpath

xml_grep pochodzi z modułu PerlaXML::Twig

xidel jest xidel

saxon-lint using SaxonHE 9.6 ,XPath 3.x (+kompatybilność retro)

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
Author: Gilles Quenot,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-25 17:16:31

Jednym pakietem, który najprawdopodobniej zostanie zainstalowany w systemie jest już python-lxml. Jeśli tak, jest to możliwe bez instalowania dodatkowego pakietu:

python -c "from lxml.etree import parse; from sys import stdin; print '\n'.join(parse(stdin).xpath('//element/@attribute'))"
 15
Author: clacke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-18 07:53:00

Możesz również spróbować mojego Xidel . Nie znajduje się w pakiecie w repozytorium, ale można go po prostu pobrać ze strony internetowej(nie ma zależności).

Ma prostą składnię dla tego zadania:

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

I jest to jedno z rzadkich z tych narzędzi, które obsługuje XPath 2.

 15
Author: BeniBela,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-15 19:38:01

Saxon zrobi to nie tylko dla XPath 2.0, ale także dla XQuery 1.0 i (w wersji komercyjnej) 3.0. Nie jest to pakiet Linuksowy, ale plik jar. Składnia (którą można łatwo zawinąć w prosty skrypt) to

java net.sf.saxon.Query -s:source.xml -qs://element/attribute
 10
Author: Michael Kay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-17 16:55:38

W moim poszukiwaniu do zapytania maven pom.pliki xml, które uruchomiłem na to pytanie. Miałem jednak następujące ograniczenia:

  • musi działać na różnych platformach.
  • musi istnieć na wszystkich głównych dystrybucjach Linuksa bez dodatkowej instalacji modułu
  • musi obsługiwać złożone pliki xml, takie jak maven pom.pliki xml
  • składnia prosta

Próbowałem wielu z powyższych bez powodzenia:

  • python lxml.etree nie jest częścią standardowego Pythona Dystrybucja
  • xml.etree jest, ale nie obsługuje złożonego maven pom.pliki xml dobrze, nie wykopali wystarczająco głęboko
  • python xml.etree nie obsługuje maven pom.pliki xml z nieznanego powodu
  • xmllint również nie działa, core dumps często na ubuntu 12.04 "xmllint: using libxml version 20708"

Jedynym rozwiązaniem, na które natknąłem się, które jest stabilne, krótkie i działa na wielu platformach i które jest Dojrzałe, jest lib rexml wbudowany w ruby:

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

Co zainspirowały mnie do znalezienia tego były następujące artykuły:

 9
Author: Mike,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-25 14:20:50

Możesz być również zainteresowany xsh . Posiada interaktywny tryb, w którym możesz robić co chcesz z dokumentem:

open 1.xml ;
ls //element/@id ;
for //p[@class="first"] echo text() ;
 5
Author: choroba,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-26 09:50:43

Odpowiedź Clacke ' a jest świetna, ale myślę, że działa tylko wtedy, gdy źródłem jest dobrze uformowany XML, a nie normalny HTML.

Więc zrobić to samo dla normalnej zawartości WWW-dokumenty HTML, które niekoniecznie są dobrze uformowane 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'))"

I zamiast tego użyj html5lib (aby zapewnić takie samo parsowanie jak przeglądarki internetowe - ponieważ podobnie jak parsery przeglądarek, html5lib jest zgodny z wymaganiami parsowania w specyfikacji 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
Author: sideshowbarker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:41

Oprócz XML:: XSH i XML:: XSH2 istnieją pewne grep - podobne narzędzia do App::xml_grep2 oraz XML::Twig (który obejmuje xml_grep zamiast xml_grep2). Mogą one być bardzo przydatne podczas pracy na dużych lub licznych plikach XML dla szybkich onelinerów lub celów Makefile. XML::Twig jest szczególnie miły do pracy dla perl podejście Skryptowe, gdy chcesz trochę więcej przetwarzania niż $SHELL i xmllint xstlproc oferta.

Schemat numeracji w nazwy aplikacji wskazują, że " 2 " wersje są nowszymi / późniejszymi wersjami zasadniczo tego samego narzędzia, które może wymagać późniejszych wersji innych modułów (lub samego perl).

 2
Author: G. Cito,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-05 16:37:31

Podobnie jak odpowiedzi Mike 'a i clacke' a, tutaj jest python one-liner (używając Pythona >= 2.5), aby uzyskać wersję kompilacji z pom.plik xml, który obejdzie fakt, że pom.pliki xml zwykle nie mają dtd lub domyślnej przestrzeni nazw, więc nie są dobrze uformowane w 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)"

Przetestowany na Macu i Linuksie i nie wymaga żadnych dodatkowych pakietów do zainstalowania.

 2
Author: pdr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-20 23:53:24

Należy wspomnieć, że sama nokogiri jest dostarczana z narzędziem wiersza poleceń, które powinno być zainstalowane z gem install nokogiri.

Możesz znaleźć ten wpis na blogu przydatny .

 2
Author: Geoff Nixon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-23 07:29:57

Wypróbowałem kilka narzędzi XPath z linii poleceń i kiedy zdałem sobie sprawę, że spędzam zbyt dużo czasu na googlowaniu i zastanawianiu się, jak one działają, więc napisałem najprostszy możliwy parser XPath w Pythonie, który zrobił to, czego potrzebowałem.

Poniższy skrypt pokazuje wartość ciągu znaków, jeśli wyrażenie XPath jest przetwarzane na łańcuch znaków, lub pokazuje cały podnód XML, jeśli wynikiem jest węzeł:

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

Używa lxml - szybkiego parsera XML napisanego w C, który nie jest zawarty w standardowym Pythonie biblioteka. Zainstaluj go za pomocą pip install lxml. Na Linuksie / OSX może wymagać prefiksu z sudo.

Użycie:

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

Lxml może również akceptować URL jako wejście:

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

Wyodrębnij atrybut url pod węzłem enclosure tj. <enclosure url="http:...""..>):

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

Xpath w Google Chrome

Jako niepowiązana uwaga: jeśli przypadkiem chcesz uruchomić wyrażenie XPath ze znacznikami strony internetowej, możesz to zrobić bezpośrednio z narzędzi DevTools Chrome: kliknij prawym przyciskiem myszy stronę w Chrome > wybierz Sprawdź, a następnie w konsoli DevTools wklej wyrażenie XPath jako $x("//spam/eggs").

Pobierz wszystkich autorów na tej stronie:

$x("//*[@class='user-details']/a/text()")
 1
Author: ccpizza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-05 00:37:56
 1
Author: mgrandi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-20 00:06:40

Oto jeden przypadek użycia xmlstarlet do wyodrębniania danych z zagnieżdżonych elementów elem1, elem2 do jednego wiersza tekstu z tego typu XML (pokazującego również, jak obsługiwać przestrzenie nazw):

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

Wyjście będzie

0.586 10.586 cue-in outro

W tym fragmencie,- m dopasowuje zagnieżdżone elem2, - v wypisuje wartości atrybutów (z wyrażeniami i względną adresacją), - o dosłowny tekst, - N dodaje nową linię:

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

Jeśli potrzeba więcej atrybutów z elem1, można to zrobić w ten sposób (również pokazując concat() Funkcja):

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

Zwróć uwagę na (IMO niepotrzebne) komplikacje z przestrzeniami nazw (NS, zadeklarowane przez-N), które sprawiły, że prawie zrezygnowałem z XPath i xmlstarlet i pisałem szybki konwerter ad-hoc.

 0
Author: diemo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-17 11:24:55