使用XMLStarlet在XML值中替换子串

时间:2015-08-05 14:23:07

标签: xml bash xpath cdata xmlstarlet

我正在尝试编辑/更新一些复杂的XML,遗憾的是我无法更改格式。我在Bash脚本中使用XMLStarlet。

我在苦苦挣扎的时候,就是当我尝试检索或编辑属性"name={name}"不唯一的属性的CDATA值并返回多个值时。

例如,我有以下XML:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?xml-stylesheet type="text/xsl" href="key.xsl" ?>
<tables>
  <tableset>
    <table name="table1">
      <row>
        <fld name="fileName">
          <strval><![CDATA[/my/XYZ/file1]]></strval>
        </fld>
        <fld name="fileName">
          <strval><![CDATA[/my/XYZ/file2]]></strval>
        </fld>
        <fld name="fileName">
          <strval><![CDATA[/my/other/XYZ/file3]]></strval>
        </fld>
        <fld name="worksBecauseUnique">
          <strval><![CDATA[/XYZ/unique]]></strval>
        </fld>
      </row>
    </table>
  </tableset>
</tables>

使用XMLStarlet时,我可以轻松编辑以下值:

xmlstarlet ed -L \
  -u '//tables/tableset/table/row/fld[@name="worksBecauseUnique"]/strval/text()' \
  -v '/ABC/unique' \
  myxmlfile.xml

但是,在尝试使用属性name=fileName修改任何CDATA值时,我遇到了问题,因为fileName在XML中出现不止一次。

我希望输出如下:

<fld name="fileName">
  <strval><![CDATA[/my/ABC/file1]]></strval>
</fld>
<fld name="fileName">
  <strval><![CDATA[/my/ABC/file2]]></strval>
</fld>
<fld name="fileName">
  <strval><![CDATA[/my/other/ABC/file3]]></strval>
</fld>

问题是,如果我尝试使用XMLStarlet更新fileName的CDATA值,如何在不更新所有内容的情况下更新每个值?

例如,如果我跑:

xmlstarlet ed -L \
  -u "//tables/tableset/table/row/fld[@name=\"fileName\"]/strval/text()" \
  -v "/my/ABC/file1" \
  myxmlfile.xml

我得到以下不正确的输出:

<fld name="fileName">
  <strval><![CDATA[/my/ABC/file1]]></strval>
</fld>
<fld name="fileName">
  <strval><![CDATA[/my/ABC/file1]]></strval>
</fld>
<fld name="fileName">
  <strval><![CDATA[/my/ABC/file1]]></strval>
</fld>

注意每个CDATA值如何设置为"/my/ABC/file1"我需要"/my/ABC/file1""/my/ABC/file2""/my/other/ABC/file3"

我希望以某种方式允许某人单独修改每个值... 希望使用任何支持XPath的工具都可以实现这一点。

非常感谢任何帮助!

1 个答案:

答案 0 :(得分:1)

提供一个表达式(带-x),将输入修改为您想要的输出:

xmlstarlet ed \
  -u '//fld/strval[contains(., "/XYZ/")]' \
  -x 'concat(substring-before(., "/XYZ/"), "/ABC/", substring-after(., "/XYZ/"))'
  <in.xml >out.xml

顺便提一下,在较新版本的XPath标准中有更好的字符串替换功能;因为libxml(由XMLStarlet使用)仅支持1.0版本,所以表达式有点不像其他情况那样笨拙。