首页 > 解决方案 > xmlstarlet foreach 节点更新值

问题描述

我正在尝试使用 xmlstarlet 更新具有相同模式的所有节点。给定以下 xml

<root>
  <application>
    <provider name="alpha" value="my.corp.lion" />
    <provider name="beta" value="my.corp.tiger" />
    <provider name="gamma" value="my.corp.monkey" />
  </application>
</root>

我目前可以按如下方式更新每个节点

oldCorp="my.corp"
newCorp="new.my.corp"
myNode="/root/application/provider[@name='alpha']/@value"
oldValue=$(xml sel -t -v ${myNode} MyXml.xml)
newValue=${oldValue//$oldCorp/$newCorp}
xml ed --inplace -u ${myNode} -v "${newValue}" MyXml.xml

# results in provider.alpha being new.my.corp.lion

但是,我希望能够做的是 foreach ALL 提供程序节点并将最终的 xml 结果更新为

<root>
  <application>
    <provider name="alpha" value="new.my.corp.lion" />
    <provider name="beta" value="new.my.corp.tiger" />
    <provider name="gamma" value="new.my.corp.monkey" />
  </application>
</root>

有没有办法对 进行 foreachproviders并将所有my.corp实例替换为new.my.corp

标签: xmlreplacexmlstarlet

解决方案


一般来说,-x像这样使用 xmlstarlet 的选项:

xmlstarlet ed --inplace -u "/root/application/provider/@value" -x "concat('new.my.corp',substring-after(.,'my.corp'))" input.xml

在您的特殊情况下,将代码更改为

oldCorp="my.corp"
newCorp="new.my.corp"
myNode="/root/application/provider/@value"
xml ed --inplace -u "${myNode}" -x "concat('${newCorp}',substring-after(.,'${oldCorp}'))" MyXml.xml

输出如所愿:

<?xml version="1.0"?>
<root>
  <application>
    <provider name="alpha" value="new.my.corp.lion"/>
    <provider name="beta" value="new.my.corp.tiger"/>
    <provider name="gamma" value="new.my.corp.monkey"/>
  </application>
</root>

此代码将myNodeXPath指定的所有属性值替换-x为 xmlstarlet 参数中构造的值。

AFAIK xmlstarlet 不支持正则表达式,因此您必须使用 XPath-1.0 函数(如 等)创建替换substring-after(...)表达式substring(...)


如果您需要 RegEx 来替换,您必须使用 XSLT-2.0 中的 XPath-2.0 函数。然后,您可以使用如下的 XSLT-2.0 样式表来实现相同的目的,但使用 RegEx'es:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" encoding="UTF-8" indent="yes"/>
  <xsl:param name="oldCorp" select="'my\.corp'" />
  <xsl:param name="newCorp" select="'new.my.corp'" />

  <!-- Identity template -->
  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*" />
    </xsl:copy>
  </xsl:template>  

  <xsl:template match="provider">
    <xsl:copy>
      <xsl:copy-of select="@*" />
      <xsl:attribute name="value">
        <xsl:value-of select="replace(@value,$oldCorp,$newCorp)" />
      </xsl:attribute>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

您可以将参数作为字符串传递给 XSLT-2.0 处理器。这两个参数都使用您的默认值进行初始化。


推荐阅读