在bash脚本中提取XML值

Pet*_*ete 38 xml bash shell sed

我正在尝试从已作为变量读入我的脚本的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> 
Run Code Online (Sandbox Code Playgroud)

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

15:54:57 - George:
Run Code Online (Sandbox Code Playgroud)

我一直在使用sed命令:

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

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

15:55:17 - Jerry:
Run Code Online (Sandbox Code Playgroud)

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

jay*_*ngh 69

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

grep -oPm1 "(?<=<title>)[^<]+"
Run Code Online (Sandbox Code Playgroud)

测试:

$ 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:
Run Code Online (Sandbox Code Playgroud)


Cha*_*ffy 25

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>
Run Code Online (Sandbox Code Playgroud)

...您只能使用以下内容提取第一个标题:

xmlstarlet sel -t -m '//title[1]' -v . -n <data.xml
Run Code Online (Sandbox Code Playgroud)

尝试将sed用于这项工作很麻烦.例如,如果标题具有属性,则基于正则表达式的方法将不起作用; 不会处理CDATA部分; 将无法正确识别命名空间映射; 无法确定所记录的XML的一部分是否被注释掉; 不会忽视属性引用(例如更改Brewster &amp; JobsBrewster & Jobs),等等.

  • @Pete这是您需要在可移植性和正确性之间做出决定的情况;你不能两者兼得。在某些常见情况下(以及大量不常见情况),您接受的答案将给出明显错误的输出。 (2认同)

dou*_*own 8

我同意Charles Duffy认为正确的XML解析器是正确的方法.

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

  • $data没有被引用,所以$data受到shell的单词拆分,文件名扩展等等.其中一个后果是不保留XML片段中的间距.

因此,根据您的特定XML结构,此修改后的sed命令应该有效

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

基本上对于包含的行,title在标记之间提取文本,然后退出(所以你不提取第二个<title>)