首页 文章

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

提问于
浏览
159

对于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的 xml_grep :: Twig无法处理不返回元素的表达式,因此无法在不进一步处理的情况下提取属性值 .

编辑:

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 运行 .

13 回答

  • 2

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

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

    你应该尝试这些工具:

    • 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 开关的交互式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
    

    .

  • 5

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

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

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

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

  • 1

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

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

    Saxon不仅可以用于XPath 2.0,还可以用于XQuery 1.0和(商业版)3.0 . 它不是Linux软件包,而是jar文件 . 语法(您可以轻松地将其包装在一个简单的脚本中)是

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

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

    是什么让我发现这个是以下文章:

  • 15

    clacke’s answer很棒,但我认为只有你的源是格式良好的XML而不是普通的HTML才有效 .

    因此,对于不一定格式良好的XML的普通Web内容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'))
    
  • 2

    除了XML::XSHXML::XSH2之外,还有一些 grep 类似的实用程序很糟糕App::xml_grep2XML::Twig(其中包括 xml_grep 而不是 xml_grep2 ) . 在为快速oneliner或 Makefile 目标处理大量或大量XML文件时,这些非常有用 . 当你想要比 $SHELLxmllint xstlproc 提供更多的处理时, XML::Twig 特别适合使用 perl 脚本方法 .

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

  • 213

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

  • 16

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

    你可能会找到this blog post useful .

  • 10

    我已经尝试了几个命令行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中的页面>选择Inspect,然后在DevTools中console将您的XPath表达式粘贴为 $x("//spam/eggs") .

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

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

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

  • 1

    这是一个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转换器 .

相关问题