如何从shell执行XPath单行程序?

时间:2013-03-17 14:16:47

标签: xml shell xpath cross-platform

对于Ubuntu和/或CentOS,是否有一个包,它有一个命令行工具,可以执行像foo //element@attribute filename.xmlfoo //element@attribute < filename.xml这样的XPath单行并返回结果行线?

我正在寻找能够让我apt-get install fooyum 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");
}
来自XML :: XPath的

xpath返回太多噪音,-- NODE --attribute = "value"

来自XML :: Twig的

xml_grep无法处理不返回元素的表达式,因此无法在不进一步处理的情况下提取属性值。

编辑:

echo cat //element/@attribute | xmllint --shell filename.xml会返回类似于xpath的噪音。

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

xmllint --xpath 'string(//element/@attribute)' filename.xml返回我想要的内容,但仅限第一场比赛。

对于几乎满足这个问题的另一个解决方案,这里有一个可用于评估任意XPath表达式的XSLT(需要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运行。

17 个答案:

答案 0 :(得分:232)

您应该尝试以下工具:

  • xmlstarlet:可以编辑,选择,转换......默认情况下不安装,xpath1
  • xmllint:默认情况下通常会安装libxml2,xpath1(检查我的wrapper是否有换行符分隔输出
  • xpath:通过perl的模块XML::XPath安装,xpath1
  • xml_grep:通过perl的模块XML::Twig安装,xpath1(有限的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

答案 1 :(得分:18)

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

此任务的语法很简单:

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

这是支持XPath 2的这些工具中罕见的一种。

答案 2 :(得分:15)

一个很可能已安装在系统上的软件包已经是python-lxml。如果是这样,这可以不安装任何额外的包:

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

答案 3 :(得分:10)

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

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

答案 4 :(得分: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:using libxml version 20708”

我遇到的解决方案是稳定,简短并且可以在许多平台上运行,而且成熟的是内置于ruby中的rexml lib:

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

让我发现这篇文章的原因是以下文章:

答案 5 :(得分:5)

您可能也对xsh感兴趣。它具有交互模式,您可以使用文档执行任何操作:

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

答案 6 :(得分:4)

clacke’s answer很棒,但我认为只有在您的来源是格式良好的XML而不是普通的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'))

答案 7 :(得分:3)

类似于Mike和clacke的答案,这里是python one-liner(使用python&gt; = 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上测试过,并且不需要安装任何额外的软件包。

答案 8 :(得分:2)

值得一提的是,nokogiri本身附带了一个命令行工具,该工具应与gem install nokogiri一起安装。

您可能会找到this blog post useful

答案 9 :(得分:2)

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

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

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

该脚本不能作为Debian或Ubuntu软件包提供,但其所有依赖项都可以。

答案 10 :(得分:2)

除了XML::XSHXML::XSH2之外,还有一些grep类似的实用程序很糟糕,因为App::xml_grep2XML::Twig(包括xml_grep而非比xml_grep2)。在为快速oneliner或Makefile目标处理大量或大量XML文件时,这些功能非常有用。如果您想要比XML::Twigperl $SHELL提供更多的处理,xmllint特别适合使用xstlproc脚本方法。

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

答案 11 :(得分:1)

我已经尝试了几个命令行XPath实用程序,当我意识到我花了太多时间在Google上搜索并弄清楚它们是如何工作的,所以我在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中的页面&gt;选择Inspect,然后在DevTools控制台中将XPath表达式粘贴为$x("//spam/eggs")

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

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

答案 12 :(得分:1)

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

答案 13 :(得分: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

答案 14 :(得分:0)

这是一个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,并编写了一个快速的ad-hoc转换器。

答案 15 :(得分:0)

安装BaseX数据库,然后像这样使用它的"standalone command-line mode"

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

basex -i filename.xml //element@attribute

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

答案 16 :(得分:0)

很抱歉成为争吵中的另一个声音。我尝试了这个线程中的所有工具,但发现它们都不能满足我的需求,所以我写了自己的。您可以在这里找到它:https://github.com/charmparticle/xpe

它已上传到 pypi,因此您可以像这样使用 pip3 轻松安装它:

sudo pip3 install xpe

安装后,您可以使用它来针对各种输入运行 xpath 表达式,其灵活性与在 selenium 或 javascript 中使用 xpath 时获得的灵活性相同。是的,你可以使用 xpaths 来对抗 HTML。