1

Find a specific xml tag and replace the text inside tags to some parameterized value. Multiple occurrences need to be replaced.

Sample file content:

<a>abc</a>

Current attempt:

sed -i "s/\(<a>\).*\(<\/a>\)/\(<a>\)$param\(<\/a>\)/g" script.xml

Desired outcome: if param=111 then

<a>111</a>
1
  • 1
    Welcome to the site. Your post is rather hard to understand as it is. Please use proper formatting to highlight (1) sample input (2) what you already tried (3) where you ran into problems - it is currently not clear what your question/problem actually is.
    – AdminBee
    Jun 19, 2020 at 12:49

4 Answers 4

2

I know you asked for a sed solution, but I generally recommend an XML-based tool (of which there are a number) instead of sed when dealing with an XML document other than a very simple one.

Suppose this is my XML document (doc.xml):

<?xml version="1.0"?>
<xml>
    <c><a>abc</a></c>
    <b>
        <a>abc</a>
        <a>abc</a>
    </b>
    <a parm="FPM">abc</a>
</xml>

I am going to use the following XML stylesheet, style.xsl, to transform the document into the desired format.

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name = "value" />

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

    <xsl:template match="a/text()[.='abc']">
        <xsl:value-of select = "$value" />
    </xsl:template>

</xsl:stylesheet>

I use the xsltproc tool, which is installed by default on most major Linux distributions, to perform the transformation:

$ xsltproc --stringparam value "111" style.xsl doc.xml
<?xml version="1.0"?>
<xml>
    <c><a>111</a></c>
    <b>
        <a>111</a>
        <a>111</a>
    </b>
    <a parm="FPM">111</a>
</xml>
$
2

My preferred tool to edit XML files is xmlstarlet. Suppose this is my XML document (doc.xml):

<?xml version="1.0"?>
<xml>
    <c><a>abc</a></c>
    <b>
        <a>abc</a>
        <a>abc</a>
    </b>
    <a parm="FPM">abc</a>
</xml>

Here's the xmlstarlet command:

param=111
xmlstarlet edit --ps --update '//a' --value "$param" doc.xml

Output

<?xml version="1.0"?>
<xml>
    <c><a>111</a></c>
    <b>
        <a>111</a>
        <a>111</a>
    </b>
    <a parm="FPM">111</a>
</xml>
1
  1. Those \(\) are superfluous in your case. Just write sed -i "s/<a>.*<\/a>/<a>$param<\/a>/g" script.xml
  2. If your string contains /, it's easier to read if you use a different separator char: sed -i "s_<a>.*</a>_<a>$param</a>_g" script.xml
  3. The g option suggests that there could be more than one <a> tag on one line. This is a problem: The .* will match the next </a>, more stuff and the last <a>, so prevent to include other tags: sed -i "s_<a>[^<]*</a>_<a>$param</a>_g" script.xml
  4. Please note that this will not work if other tags are nested inside the <a> tag or if $param contains special characters like " or \
1

I would do this in Perl instead of sed since Perl supports non-greedy matches:

perl -i -pe "s|<a>.+?</a>|<a>$param</a>|g" file

The .+? means "match the shortest possible string", so this will find the shortest string between an <a> and an </a>.

I must stress, however, that this is very likely to fail on even slightly complex XML documents and you really should consider using a dedicated XML parser instead.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .