使用 sed 或 awk 在 XML 文件中搜索替换

使用 sed 或 awk 在 XML 文件中搜索替换

因此,我有一个任务,必须通过 bash shell 脚本操作 XML 文件。

步骤如下:

  1. 查询 XML 文件的值。
  2. 获取该值并交叉引用它以从列表中查找新值。
  3. 将不同元素的值替换为新值。

以下是删除了非必要信息的 XML 示例:

<fmreq:fileManagementRequestDetail xmlns:fmreq="http://foobar.com/filemanagement">
      <fmreq:property>
         <fmreq:name>form_category_cd</fmreq:name>
         <fmreq:value>Memos</fmreq:value>
      </fmreq:property>
      <fmreq:property>
         <fmreq:name>object_name</fmreq:name>
         <fmreq:value>Correspondence</fmreq:value>
      </fmreq:property>
</fmreq:fileManagementRequestDetail>

我必须从 object_name 下的 value 元素获取值,交叉引用它,然后用新值替换 form_category_cd 值元素下的值:

因此,如果 object_name -> value 是 Correspondence,那么 form_category_cd -> value 可能需要是 YYZ。

问题是,我只能使用我们服务器上可用的工具,因为我们的运营团队限制我们只能使用手头的工具。这是一场争取 xmllint 更新的斗争,但后来被否决了。我使用的版本不支持 --xpath,这相信我在美好的一天很难。另外,我可用的版本不支持名称空间,因此 xmllint 已退出。

我尝试过 sed,但它似乎不喜欢我的正则表达式,尽管我尝试的每个测试器都工作正常。

正则表达式:

(<fmreq\:name>object_name<\/fmreq\:name>)(?:\n\s*)(<fmreq\:value>)(.*)(<\/fmreq\:value>)

我需要获取组 #3,但 sed 不会返回它。相反,它返回 XML 文件的全部内容。

sed -e 's/\(<fmreq\:name>object_name<\/fmreq\:name>\)\(?:\n\s*\)\(<fmreq\:value>\)\(.*\)\(<\/fmreq\:value>\)/\3/' < c3.xml 

我对 awk / gawk 不太熟悉,所以我也在努力弄清楚它们,但如果能找到解决方案,我对它们持开放态度。

很想有一个 awk / gawk 解决方案,只是为了让老板高兴,因为他是 awk 的老粉丝,但我会接受我能得到的,因为我被难住了。

我再次必须使用手头的工具,并且无法安装任何新的东西。

答案1

使用XML小星

$ xml ed -u '//fmreq:property[fmreq:name="object_name"]/preceding-sibling::fmreq:property/fmreq:name' -v YYZ file.xml
<?xml version="1.0"?>
<fmreq:fileManagementRequestDetail xmlns:fmreq="http://foobar.com/filemanagement">
  <fmreq:property>
    <fmreq:name>YYC</fmreq:name>
    <fmreq:value>Memos</fmreq:value>
  </fmreq:property>
  <fmreq:property>
    <fmreq:name>object_name</fmreq:name>
    <fmreq:value>Correspondence</fmreq:value>
  </fmreq:property>
</fmreq:fileManagementRequestDetail>

XPath 的第一部分//fmreq:property[fmreq:name="object_name"]将定位<fmreq:name>object_name</fmreq:name>节点,该/preceding-sibling::fmreq:property/fmreq:name位将定位<fmreq:name>前一个<fmreq:property>节点的节点。

答案2

我认为您的命令存在几个问题sed

  • 您不使用该-n选项,因此默认情况下sed仅将输入的每一行打印到输出(可能由sed命令修改)。

  • 您不需要重定向< c3.xml,因为sed将最后一个参数识别为文件名。

  • sed不太适合多行匹配。参见示例这里

以下似乎适用于您的示例:

sed -n "/<fmreq:name>object_name<\/fmreq:name>/ {n;p}" c3.xml | sed "s/^\s*<fmreq:value>\(.*\)<\/fmreq:value>/\1/g"

或者,仅sed调用一次:

sed -n "/<fmreq:name>object_name<\/fmreq\:name>/ {n;s/^\s*<fmreq:value>\(.*\)<\/fmreq:value>/\1/g;p}" c3.xml

该命令的作用细分:

  • 该选项-n指示sed在处理完该行后不打印模式空间。因此,您需要p明确使用该命令来执行此操作。

  • /regex/告诉sed只执行匹配的行上的命令regex

  • sed命令n将模式空间的内容替换为下一行输入,该输入包含您感兴趣的值。

  • sed命令将模式空间中s/regex/replacement/的第一个匹配项替换为。regexreplacement

  • sed命令p打印该行。

相关内容