如何从Shell执行XPath单行?


192

是否有针对Ubuntu和/或CentOS的软件包,其中包含一个命令行工具,该命令行工具可以像foo //element@attribute filename.xml或那样执行XPath单行代码foo //element@attribute < filename.xml并逐行返回结果?

我正在寻找可以使我公正apt-get install fooyum install foo然后就可以直接使用的东西,不需要包装或其他适应方法。

以下是一些即将发生的事情的示例:

能吉里 如果我编写此包装器,则可以按上述方式调用包装器:

#!/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的XML无法处理不返回元素的表达式,因此,如果不进行进一步处理,就不能将其用于提取属性值。

编辑:

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表达式(需要XSLT处理器中的dyn:evaluate支持):

<?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是一个很好的问题,也是关于寻找一种简单可靠的方法来在换行符上打印多个结果的集思广益的+1
Gilles Quenot

1
请注意,“噪声”来自xpathSTDERR,而不是STDOUT。
miken32 '17

@ miken32不,我只需要输出值。hastebin.com/ekarexumeg.bash
clacke

Answers:


271

您应该尝试以下工具:

  • xmlstarlet :可以编辑,选择,转换...默认情况下未安装,xpath1
  • xmllint:通常默认情况下与libxml2-utilsxpath1 一起安装(请检查我的包装器--xpath打开非常旧的发行版和换行符分隔的输出(v <2.9.9)
  • xpath:通过perl的模块XML::XPathxpath1 安装
  • xml_grep:通过perl的模块XML::Twigxpath1 安装(有限的xpath使用)
  • xidel:xpath3
  • saxon-lint :我自己的项目,@Michael Kay的Saxon-HE Java库xpath3的包装

xmllint附带libxml2-utils(可与--shell开关一起用作交互式外壳)

xmlstarletxmlstarlet

xpath 带有perl的模块 XML::Xpath

xml_grep 带有perl的模块 XML::Twig

xidelxidel

saxon-lint使用SaxonHE 9.6XPath 3.x(+复古兼容性)

例如:

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


7
优秀的!xmlstarlet sel -T -t -m '//element/@attribute' -v '.' -n filename.xml正是我想要的!
clacke 2013年

2
注意:有传言说xmlstarlet被遗弃,但是现在又在积极开发中。
clacke 2013年

6
注意:的某些旧版本xmllint不支持命令行参数--xpath,但大多数似乎都支持--shell。较脏的输出,但在绑定中仍然有用。
kevinarpe 2015年

我似乎仍然难以查询节点内容,而不是属性。谁能提供一个例子吗?出于某种原因,我仍然发现xmlstarlet很难弄清楚,并且很难在匹配,值,根目录之间找到正确的位置,以便仅查看文档结构等。即使使用sel -t -m ... -v ...此页面上的第一个示例:arstechnica.com/information-technology/2005 / 11 / Linux的2分之20051115,匹配所有,但最后一个节点,节省一个像我的使用情况下,值的表情,我似乎仍不能得到它,我只是得到空白输出..
Pysis

xpath版本上的一个不错的选择-我只是遇到了本来很好的xmllint的限制
JonnyRaa

20

您也可以尝试我的Xidel。它不在存储库中的软件包中,但是您可以从网页下载它(没有依赖项)。

它具有此任务的简单语法:

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

而且,这是支持XPath 2的这些工具中很少见的一种。


2
Xidel看起来很酷,尽管您可能应该提到您也是推荐的该工具的作者。
FrustratedWithFormsDesigner

1
Saxon和saxon-lint使用xpath3;)
Gilles Quenot

Xidel(0..8.win32.zip)在Virustotal上显示为具有恶意软件。因此,请您自担风险virustotal.com/#/file/…–
JGFMK

太棒了-我要在个人扳手工具盒中添加xidel
maoizm

15

已经很可能已经在系统上安装了一个软件包python-lxml。如果是这样,则无需安装任何额外的程序包就可以实现:

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

1
如何传递文件名?
Ramakrishnan Kannan

4
这适用于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并让您的外壳程序处理文件查找,打开和关闭。
clacke

10

在查询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

促使我找到这一本书的是以下文章:


1
这比问题要狭窄的标准,因此绝对可以作为答案。我敢肯定,许多遇到您的情况的人都会得到您的研究的帮助。我一直xmlstarlet是公认的答案,因为它符合我的更广泛的标准,而且确实很简洁。但是我可能会不时使用您的解决方案。
clacke 2014年

2
我要补充一点,以避免对结果使用引号putsp在Ruby命令中使用代替。
TomG

10

Saxon不仅针对XPath 2.0,而且针对XQuery 1.0和(商业版本)3.0都这样做。它不是Linux软件包,而是jar文件。语法(您可以轻松地将其包装在一个简单的脚本中)是

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

2020更新

Saxon 10.0包括Gizmo工具,该工具可以交互使用,也可以从命令行中批量使用。例如

java net.sf.saxon.Gizmo -s:source.xml
/>show //element/@attribute
/>quit

SaxonB是在Ubuntu,包libsaxonb-java,但如果我跑saxonb-xquery -qs://element/@attribute -s:filename.xml我得到SENR0001: Cannot serialize a free-standing attribute node,同样的问题与如xml_grep
clacke 2013年

3
如果要查看此查询选择的属性节点的完整详细信息,请在命令行上使用-wrap选项。如果只需要属性的字符串值,则将/ string()添加到查询中。
Michael Kay 2013年

谢谢。添加/ string()更近了。但是它输出一个XML标头并将所有结果放在一行上,因此仍然没有雪茄。
clacke,2013年

2
如果您不想使用XML标头,请添加选项!method = text。
Michael Kay

要使用命名空间,请将其添加为-qs'-qs:declare namespace mets="http://www.loc.gov/METS/";/mets:mets/mets:dmdSec'
igo

5

您可能也对xsh感兴趣。它具有交互模式,您可以在其中处理文档:

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

它似乎没有作为软件包提供,至少在Ubuntu中没有。
clacke 2013年

1
@clacke:不是,但是可以通过CPAN从CPAN安装cpan XML::XSH2
choroba

@choroba,我已经在OS X上尝试过,但是由于某种makefile错误而无法安装。
cnst 2014年

@cnst:您是否已安装XML :: LibXML?
choroba 2014年

@choroba,我不知道;但我的意思是,cpan XML::XSH2无法安装任何东西。
cnst 2014年

5

clacke的答案很好,但我认为只有在您的源代码格式正确,而不是常规HTML格式时,才能起作用。

因此,对于普通的Web内容(不一定是格式正确的XML的HTML文档)执行相同的操作:

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(以确保您获得与Web浏览器相同的解析行为-因为像浏览器解析器一样,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'))

是的,在这个问题上,我有一个自己的假设,即XPath隐含XML。此答案是对此处其他答案的一个很好的补充,感谢您让我了解html5lib!
clacke

3

与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上进行了测试,不需要安装任何额外的软件包。


2
我今天用这个!我们的构建服务器既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()似乎没有必要。
clacke

2

除了XML :: XSHXML :: XSH2之外,还有一些类似grep的实用工具suck as App::xml_grep2XML::Twig(包括xml_grep而不是xml_grep2)。当为快速的单行或Makefile目标处理大型或大量XML文件时,这些功能非常有用。 XML::Twig是特别好与一个合作perl编写脚本的方法,当你想比你更AA位处理$SHELLxmllint xstlproc报价。

应用程序名称中的编号方案表示“ 2”版本是本质上相同工具的较新/更高版本,可能需要其他模块(或其perl本身)的更高版本。


xml_grep2 -t //element@attribute filename.xml可以正常工作并达到我的预期(xml_grep --root //element@attribute --text_only filename.xml仍然没有,返回“无法识别的表达式”错误)。大!
clacke 2014年

xml_grep --pretty_print --root '//element[@attribute]' --text_only filename.xml呢 不确定[]在这种情况下发生了什么或XPath在说什么,但是@attribute用方括号将xml_grep和包围 起来对and 起作用xml_grep2
G. Cito 2014年

我的意思//element/@attribute不是//element@attribute。显然不能对其进行编辑,但是请不要将其保留在此处,而要删除并替换它,以免混淆此讨论的历史。
clacke 2014年

//element[@attribute]选择element具有属性的类型的元素attribute。我不想要元素,只想要属性。<element attribute='foo'/>应该给我foo,而不是全部<element attribute='foo'/>
clacke 2014年

...并且--text_only在这种情况下,如果元素<element attribute='foo'/>内部没有文本节点,则为我提供空字符串。
clacke 2014年


2

我尝试了几个命令行XPath实用程序,当我意识到自己花了很多时间在搜索和弄清它们的工作方式时,因此我用Python编写了最简单的XPath解析器,该解析器可以满足我的需要。

如果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了一个用C编写的快速XML解析器,它没有包含在标准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"

Google Chrome中的Xpath

与此无关的是:如果偶然要针对网页的标记运行XPath表达式,则可以直接通过Chrome devtools进行操作:右键单击Chrome中的页面>选择“检查”,然后在DevTools中控制台将XPa​​th表达式粘贴为$x("//spam/eggs")

在此页面上获取所有作者:

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

不是一个衬垫,并lxml在已经提到2其他答案你的前几年。
clacke

2

这是一个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声明)的(IMO不必要的)复杂性,使我几乎放弃了xpath和xmlstarlet,而是编写了一个快速的即席转换器。


xmlstarlet很棒,但是公认的主要排名答案已经提到了它。如果有的话,有关如何处理名称空间的信息可能已经与注释相关。任何遇到名称空间和xmlstarlet问题的人都可以在文档中
clacke,

2
当然,@ clacke,xmlstarlet已经被提及过几次了,但是也很难理解,而且文档不足。我在想一个小时,如何从嵌套元素中获取信息。我希望我有那个示例,所以我在这里发布它以避免其他人浪费时间(并且该示例对于评论来说太长了)。
diemo '18

2

我的Python脚本xgrep.py正是这样做的。为了搜索文件attribute中元素的所有属性,您可以按以下方式运行它:elementfilename.xml ...

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

有各种用于控制输出的开关,例如-c用于计数匹配,-i缩进匹配的部分以及-l仅输出文件名。

该脚本无法作为Debian或Ubuntu软件包使用,但其所有依赖项均可用。


您正在sourcehut上托管!真好!
clacke

1

由于该项目显然是一个新项目,请查看https://github.com/jeffbr13/xq,似乎是一个包装lxml,但这就是您真正需要的(并在其他答案中也使用了lxml发布了临时解决方案)


1

我对用于HTML XPath查询的Python单一代码感到不满意,所以我写了自己的代码。假设您已安装python-lxml软件包或已运行pip install --user lxml

function htmlxpath() { python -c 'for x in __import__("lxml.html").html.fromstring(__import__("sys").stdin.read()).xpath(__import__("sys").argv[1]): print(x)' $1 }

有了它之后,就可以在以下示例中使用它:

> curl -s https://slashdot.org | htmlxpath '//title/text()'
Slashdot: News for nerds, stuff that matters

0

安装BaseX数据库,然后使用它的“独立命令行模式”,如下所示:

basex -i - //element@attribute < filename.xml

要么

basex -i filename.xml //element@attribute

查询语言实际上是XQuery(3.0),而不是XPath,但是由于XQuery是XPath的超集,因此您无需注意即可使用XPath查询。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.