从命令行将XPath列出到XML节点

时间:2012-08-17 19:54:47

标签: xml macos bash xpath

鉴于这个大型深层嵌套XML文档(bookstore.xml)的片段,我想知道amazon节点的完整路径。如何从命令行打印该路径?

<bookstore>
<book>
  <title lang="eng">Learning XML</title>
  <price>
    <retail>39.95</retail>
    <discounts>
      <amazon>29.99</amazon>
    </discounts>
    <currency>USD</currency>
  </price>
</book>
...
</bookstore>

理想情况下,它看起来像这样:

old-gregg$ magic bookstore.xml amazon
/bookstore/book/price/discounts/amazon

3 个答案:

答案 0 :(得分:8)

我找到了XMLStarlet,它正是我在这里寻找的。要使用Homebrew安装它:

$ brew update
$ brew install xmlstarlet
$ xml el bookstore.xml | grep amazon
/bookstore/book/price/discounts/amazon

答案 1 :(得分:5)

使用xmllint这是与libxml2捆绑在一起的命令行工具。很可能它在您的系统上可用。

根据您的示例数据(删除省略号),我玩了并管理了以下内容:

echo -e "du\nbye\n" | \
  xmllint --shell data

返回

/ > du
/
bookstore
  book
    title
    price
      retail
      discounts
        amazon
      currency
/ > bye

这使用工具的交互模式 du要求从当前节点(此处为root)开始打印整个子树。 bye只退出该计划。

下一步是解析此输出。

<强>更新: (假设XML在data)中 请注意,有问题的节点目前是硬编码的!

#!/bin/bash

echo -e "du\nbye\n" | \
  xmllint --shell data | \
  sed 's/  /: /g' | \
  awk '
    BEGIN {depth = 0}
    $NF == "amazon" {
      for(i=1; i<NF; i++) {printf("/%s", STACK[i])}
      print "/" $NF
    }
    /^\// {next}
    NF == depth + 1 {depth = NF; STACK[depth] = $NF; next}
    NF == depth {STACK[depth] = $NF; next}
    NF < depth {depth = NF; STACK[depth] = $NF; next}
    1 {print "something went horribly wrong!"}
  '

给出

/bookstore/book/price/discounts/amazon

解释一下sed命令后的输出:

/ > du
/
bookstore
: book
: : title
: : price
: : : retail
: : : discounts
: : : : amazon
: : : currency
/ > bye

sed[two spaces]替换[:space] 在下文中,使用awk检测深度很简单。

答案 2 :(得分:0)

在XPath 2.0中,您可以使用//amazon选择元素/ancestor-or-self::*/node-name(.)以获取父节点名称,并string-join(..., "/")从中获取路径。

最后是XPath 2.0表达式

string-join(("",//amazon/ancestor-or-self::*/node-name(.)),"/")

将准确返回您想要的路径。 (虽然它不会添加[]属性测试,如果你也需要它们)

我不知道是否有任何其他XPath 2.0命令行工具,但我几天前做了自己的。如果您碰巧有fpc,可以下载source并编译它(没有二进制编辑:现在它们在那里链接:http://videlibri.sourceforge.net/xidel.html)。 有了它,你可以运行:

 xidel /tmp/so2.xml --extract 'string-join(("",//amazon/ancestor-or-self::*/node-name(.)),"/")'

我也做了一个你可以尝试的CGI服务:

  wget -qO - 'http://videlibri.sourceforge.net/cgi-bin/xidelcgi?extract=string-join(("",//amazon/ancestor-or-self::*/node-name(.)),"/")&data=<bookstore><book>  <title lang="eng">Learning XML</title>  <price>   <retail>39.95</retail>    <discounts>      <amazon>29.99</amazon>    </discounts>    <currency>USD</currency>  </price></book></bookstore>'