Reputation: 47377
I'm trying to update all nodes with the same pattern using xmlstarlet. Given the following 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>
I can currently update each node as follows
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
What I'd like to be able to do however is foreach ALL provider nodes and update the final xml result to be
<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>
Is there a way to do a foreach over the providers
and replace all my.corp
instances with new.my.corp
?
Upvotes: 0
Views: 279
Reputation: 29022
In general, use the -x
option of xmlstarlet like this:
xmlstarlet ed --inplace -u "/root/application/provider/@value" -x "concat('new.my.corp',substring-after(.,'my.corp'))" input.xml
In your special case, change the code to
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
The output is as desired:
<?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>
This code replaces all attribute values specified by the myNode
XPath with the value constructed in the -x
argument of xmlstarlet.
AFAIK xmlstarlet does not support RegEx'es, so you have to create the replacement expressions with XPath-1.0 functions like substring-after(...)
, substring(...)
and so on.
If you need RegEx'es for your replacement, you have to use XPath-2.0 functions which are part of XSLT-2.0. Then you can use an XSLT-2.0 stylesheet like the following which achieves the same thing, but with 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>
You can pass the parameters as strings to the XSLT-2.0 processor. Both parameters are initialized with your default values.
Upvotes: 2