【问题标题】:Extract XML Value in bash script [duplicate]在bash脚本中提取XML值[重复]
【发布时间】:2013-06-24 09:35:58
【问题描述】:

我正在尝试从已作为变量读入我的脚本的 xml 文档中提取一个值。原始变量 $data 是:

<item> 
  <title>15:54:57 - George:</title>
  <description>Diane DeConn? You saw Diane DeConn!</description> 
</item> 
<item> 
  <title>15:55:17 - Jerry:</title> 
  <description>Something huh?</description>
</item> 

我希望提取第一个标题值,所以

15:54:57 - George:

我一直在使用 sed 命令:

title=$(sed -n -e 's/.*<title>\(.*\)<\/title>.*/\1/p' <<< $data)

但这只会输出第二个标题值:

15:55:17 - Jerry:

有谁知道我做错了什么? 谢谢!

【问题讨论】:

  • 您提供的测试数据不是有效的 XML 文档。您的真实数据是什么样的?
  • 无复制。我两个都懂。
  • ...为了更清楚我所说的“不是有效的 XML 文档”的意思——它没有根元素,它的标签也没有全部关闭。这很重要;旨在处理无效 XML 的答案与可以假定合法合规输入的答案不同。

标签: xml bash shell sed


【解决方案1】:

正如Charles Duffey 所说,XML 解析器最好使用适当的 XML 解析工具进行解析。对于一次性工作,以下应该有效。

grep -oPm1 "(?<=<title>)[^<]+"

测试:

$ echo "$data"
<item> 
  <title>15:54:57 - George:</title>
  <description>Diane DeConn? You saw Diane DeConn!</description> 
</item> 
<item> 
  <title>15:55:17 - Jerry:</title> 
  <description>Something huh?</description>
$ title=$(grep -oPm1 "(?<=<title>)[^<]+" <<< "$data")
$ echo "$title"
15:54:57 - George:

【讨论】:

  • perl 解决方案是:perl -ne 'print and last if s/.*&lt;title&gt;(.*)&lt;\/title&gt;.*/\1/;' &lt;&lt;&lt; "$data" 尽管正如 Charles Duffy 在 cmets 中建议的那样,xml 解析最好使用 xml 解析器完成。这应该对一次性黑客有好处。
  • @ooxi 感谢您发表评论。我不会反对你的反对票,因为我同意你的观点。我从来没有这是唯一的方法。事实上,我确实在我的 cmets 中声明应该使用 xml 解析器。但是对于一次性工作,我不希望任何人学习 xml 解析器。显然它对 OP 有效。
  • @JonasBang,也许你的意思是REL[.]P.*REL.P* 完全不同。
  • @jaypal 你是对的,它会像 OP 发布的那样一次性完成。但是由于这篇文章在搜索这个主题时在谷歌的搜索结果中名列前茅,因此人们会开始复制粘贴已接受的解决方案,我会对超出 OP 问题的正确答案感到更满意
  • @ooxi 同意。我更新了答案以发出警告并嵌入了指向查尔斯答案的链接。
【解决方案2】:

XMLStarlet 或其他 XPath 引擎是完成这项工作的正确工具。

例如,data.xml 包含以下内容:

<root>
  <item> 
    <title>15:54:57 - George:</title>
    <description>Diane DeConn? You saw Diane DeConn!</description> 
  </item> 
  <item> 
    <title>15:55:17 - Jerry:</title> 
    <description>Something huh?</description>
  </item>
</root>

...您只能提取具有以下内容的第一个标题:

xmlstarlet sel -t -m '//title[1]' -v . -n <data.xml

尝试使用 sed 完成这项工作的是 troublesome。例如,如果标题具有属性,则基于正则表达式的方法将不起作用;不会处理 CDATA 部分;不能正确识别命名空间映射;无法确定记录的 XML 的一部分是否被注释掉;不会取消转义属性引用(例如将Brewster &amp;amp; Jobs 更改为Brewster &amp; Jobs)等等。

【讨论】:

  • 谢谢 Charles,我希望脚本是可移植的,所以我认为 xmlstarlet 不是最好的,因为我需要在每个我想使用它的系统上安装包。
  • @Pete 这是一个需要在可移植性和正确性之间做出选择的情况;你不能同时拥有。您接受的答案会在某些常见情况下(以及大量不常见的情况)给出明显错误的输出。
  • 好工具,使用起来有点复杂,但经过 30 分钟的 man 和测试后,我得到了我需要的东西。谢谢!
【解决方案3】:

我同意 Charles Duffy 的观点,即正确的 XML 解析器是正确的方法。

但是至于你的sed 命令有什么问题(或者你是故意这样做的?)。

  • $data 没有被引用,所以 $data 受制于 shell 的分词、文件名扩展等。后果之一是未保留 XML sn-p 中的间距。

因此,鉴于您的特定 XML 结构,这个修改后的 sed 命令应该可以工作

title=$(sed -ne '/title/{s/.*<title>\(.*\)<\/title>.*/\1/p;q;}' <<< "$data")

基本上对于包含title的行,提取标签之间的文本,然后退出(这样就不会提取第二个&lt;title&gt;

【讨论】:

  • 我在 sed 命令的 'q' 后面加了一个分号。这仍然适用于 Linux 发行版,并解决了 MacOS 上出现错误的问题:"extra characters at the end of q command"
猜你喜欢
  • 2019-02-15
  • 1970-01-01
  • 2016-12-25
  • 2018-01-14
  • 1970-01-01
  • 1970-01-01
  • 2021-04-01
  • 2016-04-18
  • 1970-01-01
相关资源
最近更新 更多