Есть ли там пакет для Ubuntu и/или CentOS, у которого есть инструмент командной строки, который может выполнять однострочный интерфейс XPath, например 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
.
Вам следует попробовать следующие инструменты:
xmlstarlet
xmllint
saxon-lint
(собственный проект) xmllint
поставляется с libxml2-utils
(может использоваться как интерактивная оболочка с помощью переключателя --shell
) xmlstarlet
is xmlstarlet
.
saxon-lint
с помощью SaxonHE 9.6 является единственным, кто запускает XPath 3.x (+ ретро-совместимость), другие - XPath 1.x.
Пример:
xmllint --xpath '//element/@attribute' file.xml
xmlstarlet sel -t -v "//element/@attribute" file.xml
saxon-lint --xpath '//element/@attribute' file.xml
xmlstarlet sel -T -t -m '//element/@attribute' -v '.' -n filename.xml
делает именно то, что я хочу!
Один пакет, который, скорее всего, будет установлен в системе, уже есть python-lxml
. Если это так, это возможно без установки дополнительного пакета:
python -c "from lxml.etree import parse; from sys import stdin; print '\n'.join(parse(stdin).xpath('//element/@attribute'))"
stdin
. Это избавляет от необходимости включать open()
и close()
в уже достаточно длинную строку. Чтобы проанализировать файл, просто запустите python -c "from lxml.etree import parse; from sys import stdin; print '\n'.join(parse(stdin).xpath('//element/@attribute'))" < my_file.xml
и пусть ваша оболочка обрабатывает поиск, открытие и закрытие файла.
Вы также можете попробовать Xidel. Он не находится в пакете в репозитории, но вы можете просто загрузить его с веб-страницы (у него нет зависимостей).
Он имеет простой синтаксис для этой задачи:
xidel filename.xml -e '//element/@attribute'
И это один из редких из этих инструментов, поддерживающий XPath 2.
Saxon сделает это не только для XPath 2.0, но и для XQuery 1.0 и (в коммерческой версии) 3.0. Он не поставляется как пакет Linux, а как файл jar. Синтаксис (который можно легко обернуть простым script) - это
java net.sf.saxon.Query -s:source.xml -qs://element/attribute
libsaxonb-java
, но если я запускаю saxonb-xquery -qs://element/@attribute -s:filename.xml
я получаю SENR0001: Cannot serialize a free-standing attribute node
, SENR0001: Cannot serialize a free-standing attribute node
же проблема, как, например, xml_grep
.
В моем запросе для запроса maven pom.xml файлов я столкнулся с этим вопросом. Однако у меня были следующие ограничения:
Я пробовал многие из вышеперечисленных без успеха:
Единственное решение, с которым я столкнулся, является стабильным, коротким и работает на многих платформах, а зрелым является rexml lib, встроенный в ruby:
ruby -r rexml/document -e 'include REXML;
p XPath.first(Document.new($stdin), "/project/version/text()")' < pom.xml
Что вдохновило меня на то, чтобы найти это, были следующие статьи:
xmlstarlet
в качестве принятого ответа, потому что он соответствует моим более широким критериям и он действительно аккуратный . Но я, вероятно, буду время от времени использовать ваше решение.
puts
вместо p
в команде Ruby.
Вас также может заинтересовать xsh. Он имеет интерактивный режим, в котором вы можете делать все, что угодно, с документом:
open 1.xml ;
ls //element/@id ;
for //p[@class="first"] echo text() ;
cpan XML::XSH2
.
ответы на вызовы - это здорово, но я думаю, что работает только в том случае, если ваш источник является хорошо сформированным XML, а не нормальным HTML.
Итак, чтобы сделать то же самое для обычного веб-контента - HTML-документы, которые arent обязательно правильно сформировали 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'))
Следует отметить, что сам nokogiri поставляется с инструментом командной строки, который должен быть установлен с помощью gem install nokogiri
.
Возможно, вы найдете этот пост в блоге.
В дополнение к XML:: XSH и XML:: XSH2 есть некоторые grep
-подобные утилиты сосать как App::xml_grep2
и XML::Twig
(который включает xml_grep
, а не xml_grep2
). Они могут быть весьма полезны при работе с большими или многочисленными XML файлами для быстрых oneliners или Makefile
целей. XML::Twig
особенно приятно работать с perl
сценарием, когда вы хотите немного больше обработки, чем предлагаете $SHELL
и xmllint
xstlproc
.
Схема нумерации в именах приложений указывает, что версии "2" представляют собой более новую/более позднюю версию, по существу, того же инструмента, который может потребовать более поздние версии других модулей (или самого perl
).
xml_grep2 -t //element@attribute filename.xml
работает и делает то, что я ожидаю ( xml_grep --root //element@attribute --text_only filename.xml
прежнему нет, возвращает ошибку «нераспознанное выражение»). Большой!
xml_grep --pretty_print --root '//element[@attribute]' --text_only filename.xml
? Не уверен, что там происходит или что XPath говорит о []
в этом случае, но окружение @attribute
квадратными скобками работает для xml_grep
и xml_grep2
.
Подобно ответам Mike и clacke, вот однострочный python (используя python >= 2.5), чтобы получить версию сборки из файла pom.xml, которая обходит тот факт, что файлы pom.xml обычно не имеют dtd или пространство имен по умолчанию, поэтому не отображаются корректно для 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 и не требует установки дополнительных пакетов.
lxml
серверах не было ни lxml
ни xmllint
, ни даже Ruby. В духе формата в своем собственном ответе я написал его как python3 -c "from xml.etree.ElementTree import parse; from sys import stdin; print(parse(stdin).find('.//element[subelement=\"value\"]/othersubelement').text)" <<< "$variable_containing_xml"
в bash. .getroot()
не кажется необходимым.
Я пробовал несколько утилит XPath командной строки, и когда я понял, что трачу слишком много времени на поиски и выясняя, как они работают, поэтому я написал простейший возможный синтаксический анализатор XPath в Python, который сделал то, что мне нужно.
В приведенном ниже script показано строковое значение, если выражение XPath оценивается как строка или отображает весь подзону XML, если результатом является node:
#!/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 в приложении node (т.е.):
python xmlcat.py xmlcat.py file.xml "//enclosure/@url"
Как несвязанная сторона примечания: если случайно вы хотите запустить выражение XPath против разметки веб-страницы, вы можете сделать это прямо из Chrome devtools: щелкните правой кнопкой мыши страницу в Chrome > выберите "Осмотр", а затем в консоли DevTools вставьте выражение XPath как $x("//spam/eggs")
.
Получить всех авторов на этой странице:
$x("//*[@class='user-details']/a/text()")
lxml
, а lxml
уже упоминался в двух других ответах за годы до вашего.
xpath
находится на STDERR, а не на STDOUT.