Как выполнить XPath one-liners из оболочки?

Есть ли там пакет для Ubuntu и/или CentOS, у которого есть инструмент командной строки, который может выполнять однострочный интерфейс XPath, например foo //[email protected] filename.xml или foo //[email protected] < 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="'&#10;'"/>
    </xsl:for-each> 
  </xsl:template>
</xsl:stylesheet>

Запустите с помощью xsltproc --stringparam pattern //element/@attribute --stringparam value . arbitrary-xpath.xslt filename.xml.

Ответ 1

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

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

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

xmlstarlet - это xmlstarlet.

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

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

xidel является xidel

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

Пример:

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

,

Ответ 2

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

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

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

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

Ответ 3

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

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

Ответ 4

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

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

Ответ 5

В своем поиске по запросу файлов 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"

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

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

Что вдохновило меня на поиск этой статьи, так это следующие статьи:

Ответ 6

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

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

Ответ 7

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

Ответ 8

Подобно ответам 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 и не требует установки дополнительных пакетов.

Ответ 9

В дополнение к 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).

Ответ 10

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

Возможно, вы найдете этот пост в блоге.

Ответ 11

Я попробовал несколько утилит командной строки 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> выберите Inspect, а затем в DevTools console вставьте ваше выражение XPath как $x("//spam/eggs").

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

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

Ответ 12

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

Ответ 13

Здесь один случай использования 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

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

Ответ 14

Мой скрипт на Python xgrep.py делает именно это. Чтобы найти все атрибуты attribute элементов element в файлах filename.xml ..., вы должны выполнить его следующим образом:

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

Существуют различные переключатели для управления выводом, такие как -c для подсчета совпадений, -i для отступа соответствующих частей и -l для вывода только имен файлов.

Сценарий недоступен как пакет Debian или Ubuntu, но все его зависимости доступны.