48

我正在尝试从已作为变量读入我的脚本的 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:

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

4

3 回答 3

92

正如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:
于 2013-06-27T02:56:46.270 回答
32

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 来完成这项工作很麻烦。例如,如果标题具有属性,则基于正则表达式的方法将不起作用;不会处理 CDATA 部分;不能正确识别命名空间映射;无法确定记录的 XML 的一部分是否被注释掉;不会取消转义属性引用(例如更改Brewster &amp; JobsBrewster & Jobs),等等。

于 2013-06-27T03:12:31.000 回答
12

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

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

  • $data没有被引用,因此$data受制于 shell 的分词、文件名扩展等。结果之一是未保留 XML 片段中的间距。

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

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

基本上对于包含的行title,提取标签之间的文本,然后退出(所以你不提取 2nd <title>

于 2013-06-27T03:28:28.830 回答