Как выполнить XPath на один-вкладыши из раковины?


есть ли там пакет для Ubuntu и / или CentOS, который имеет инструмент командной строки, который может выполнять XPath one-liner, например foo //element@attribute filename.xml или foo //element@attribute < filename.xml и возвращать результаты строка за строкой?

я ищу что-то, что позволит мне просто apt-get install foo или yum install foo а затем просто работает из коробки, без обертки или другой адаптации необходимо.

вот несколько примеров того, что близко:

Nokogiri. Если я напишу эту обертку я можно вызвать обертку описанным выше способом:

#!/usr/bin/ruby

require 'nokogiri'

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

XML:: XPath. Будет работать с этой оберткой:

#!/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 из XML::XPath возвращает слишком много шума, -- NODE -- и attribute = "value".

xml_grep из XML:: Twig не может обрабатывать выражения, которые не возвращают элементы, поэтому их нельзя использовать для извлечения значений атрибутов без дальнейшей обработки.

EDIT:

echo cat //element/@attribute | xmllint --shell filename.xml возвращает шум, подобный xpath.

xmllint --xpath //element/@attribute filename.xml возвращает attribute = "value".

xmllint --xpath 'string(//element/@attribute)' filename.xml возвращает то, что я хочу, но только на первый матч.

для другого решения, почти удовлетворяющего вопросу, вот XSLT, который можно использовать для оценки произвольных выражений XPath (требуется Dyn: оценить поддержку в процессоре 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="'
'"/>
    </xsl:for-each> 
  </xsl:template>
</xsl:stylesheet>

запустить с xsltproc --stringparam pattern //element/@attribute --stringparam value . arbitrary-xpath.xslt filename.xml.

13 156

13 ответов:

вы должны попробовать эти средства :

  • xmlstarlet : можно редактировать, выбирать, преобразовывать... Не установлен по умолчанию, xpath1
  • xmllint : часто устанавливается по умолчанию с libxml2, xpath1 (проверьте мой фантик чтобы новые строки разделяли вывод
  • xpath: устанавливается через модуль perl XML::XPath, xpath1
  • xml_grep: устанавливается через модуль perl XML::Twig, xpath1 (ограниченное использование xpath)
  • xidel: xpath3
  • saxon-lint: мой собственный проект, wrapper over @Michael Kay'S Saxon-HE Java library, xpath3

xmllint входит libxml2-utils (может использоваться как интерактивная оболочка с --shell переключатель)

xmlstarlet и xmlstarlet.

xpath поставляется с модулем perl XML::Xpath

xml_grep поставляется с модулем perl XML::Twig

xidel is xidel

saxon-lint используя SaxonHE 9.6, XPath 3.x (+ретро совместимость)

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

.

один пакет, который, скорее всего, будет установлен в системе уже python-lxml. Если да, то это возможно без установки какого-либо дополнительного пакета:

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

вы также можете попробовать мой Xidel. Он не находится в пакете в репозитории, но вы можете просто загрузить его с веб-страницы (он не имеет зависимостей).

Он имеет простой синтаксис для выполнения этой задачи:

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

и это один из редких из этих инструментов, который поддерживает XPath 2.

Saxon сделает это не только для XPath 2.0, но и для XQuery 1.0 и (в коммерческой версии) 3.0. Он поставляется не как пакет Linux, а как файл jar. Синтаксис (который вы можете легко обернуть в простой скрипт)

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

в моем поиске для запроса maven pom.xml-файлы, которые я запустил, пересекают этот вопрос. Однако у меня были следующие ограничения:

  • должен работать кросс-платформенный.
  • должен существовать на всех основных дистрибутивах linux без установки каких-либо дополнительных модулей
  • должен обрабатывать сложные xml-файлы, такие как maven pom.xml-файлы
  • простой синтаксис

Я пробовал многие из вышеперечисленных без успеха:

  • python lxml.etree не является частью стандартного дистрибутива python
  • xml.etree - это, но не обрабатывает сложный maven pom.xml файлы ну, не копали достаточно глубоко
  • python xml.etree не обрабатывает maven pom.xml файлы по неизвестной причине
  • xmllint тоже не работает, ядро часто дампы на ubuntu 12.04 "xmllint: использование libxml версии 20708"

единственное решение, с которым я столкнулся, является стабильным, коротким и работает на многих платформах и то есть зрелым является rexml lib встроенный в ruby:

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

что вдохновило меня найти этот был следующие статьи:

вы также можете быть заинтересованы в xsh. Он имеет интерактивный режим, где вы можете делать все, что вам нравится с документом:

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

ответ клака отлично, но я думаю, что работает только если ваш источник хорошо сформирован XML, а не обычный HTML.

чтобы сделать то же самое для обычного веб-контента-HTML-документов, которые не обязательно хорошо сформированы 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'))"

и вместо этого использовать html5lib (чтобы убедиться, что вы получаете то же поведение синтаксического анализа, что и веб-браузеры-потому что, как и Парсеры браузера, html5lib соответствует требованиям синтаксического анализа в спецификации 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'))

кроме XML:: XSH и XML:: XSH2 есть немного grep - как утилиты сосут как App::xml_grep2 и XML::Twig (которая включает в себя xml_grep, а не xml_grep2). Они могут быть весьма полезны при работе с большими или многочисленными XML-файлами для быстрых oneliners или Makefile цели. XML::Twig особенно приятно работать с perl скриптовый подход, когда вы хотите немного больше обработки, чем ваш $SHELL и xmllintxstlproc предложение.

схема нумерации в именах приложений указывает, что версии " 2 " являются более новыми/более поздними версиями по существу того же инструмента, который может потребовать более поздних версий других модулей (или ).

подобно ответам Майка и клака, вот python one-liner (используя python >= 2.5), чтобы получить версию сборки из pom.xml-файл, который обходит тот факт, что pom.xml-файлы обычно не имеют пространства имен dtd или default, поэтому они не выглядят хорошо сформированными для 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)"

протестировано на Mac и Linux, и не требует установки каких-либо дополнительных пакетов.

следует отметить, что сам nokogiri поставляется с инструментом командной строки, который должен быть установлен с gem install nokogiri.

вы можете найти это сообщение в блоге полезно.

я пробовал пару утилит командной строки XPath, и когда я понял, что трачу слишком много времени на поиск и выяснение того, как они работают, поэтому я написал самый простой из возможных парсеров XPath в Python, который сделал то, что мне нужно.

сценарий ниже показывает строковое значение, если выражение XPath вычисляется в строку, или показывает весь подузел XML, если результатом является узел:

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

он использует lxml - быстрый синтаксический анализатор XML, написанный на C, который не входит в стандартной библиотеке python. Установите его с помощью pip install lxml. В Linux / OSX может потребоваться префикс с sudo.

использование:

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

lxml также может принимать URL в качестве входных данных:

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

извлеките атрибут url под узлом вложения, т. е. <enclosure url="http:...""..>):

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

Xpath в Google Chrome

как несвязанная сторона примечание: если случайно вы хотите запустить выражение XPath против разметки веб-страницы, то вы можете это сделать прямо из Chrome devtools: щелкните правой кнопкой мыши страницу в Chrome > выберите проверить, а затем в консоли DevTools вставьте выражение XPath как $x("//spam/eggs").

получить всех авторов на этой странице:

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

поскольку этот проект, по-видимому, довольно новый, проверьте https://github.com/jeffbr13/xq, кажется, обертка вокруг lxml, но это все, что вам действительно нужно (и опубликованные специальные решения с использованием lxml в других ответах)

вот один случай использования xmlstarlet для извлечения данных из вложенных элементов elem1, elem2 в одну строку текста из этого типа XML (также показано, как обрабатывать пространства имен):

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

выход будет

0.586 10.586 cue-in outro

в этом фрагменте-m соответствует вложенному элементу elem2, - V выводит значения атрибутов (с выражениями и относительной адресацией), - o буквальный текст, - n добавляет новую строку:

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

если от elem1 требуется больше атрибутов, можно сделать это следующим образом (также показывает функцию 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

обратите внимание на (ИМО ненужное) осложнение с пространствами имен (ns, объявленное с-N), которое заставило меня почти отказаться от xpath и xmlstarlet и написать быстрый специальный конвертер.