Linux – Using sed to replace string with special characters in XML file

find and replacelinuxsedspecial characters

I am having difficulty getting sed to replace a string of text in an XML file, despite the fact that I have no trouble using grep to find that same string. Since the new string and old string to be replaced contain a lot of special characters, I thought it best to store them in variables as opposed to using a slew of backslashes:

OLD_STRING='<property name="webServiceHost">${jboss.bind.address}</property>'
NEW_STRING='<!--<property name="webServiceHost">${jboss.bind.address}</property>-->'

The strings appear to be stored as expected:

$ echo $OLD_STRING; echo $NEW_STRING
<property name="webServiceHost">${jboss.bind.address}</property>
<!--<property name="webServiceHost">${jboss.bind.address}</property>-->

Grep'ing confirms the old string is present in the XML file:

$ grep "$OLD_STRING" jboss-beans.xml
<property name="webServiceHost">${jboss.bind.address}</property>

But the following sed command produces no output whatsoever:

sed -i 's/"$OLD_STRING"/"$NEW_STRING"/g' jboss-beans.xml

Any idea what I'm missing here?

Best Answer

Putting shell variables in single quotes disables their interpretation. That's why your command has no effect.

$ echo  's/"$OLD_STRING"/"$NEW_STRING"/g'
s/"$OLD_STRING"/"$NEW_STRING"/g

It should be written like that:

sed -i "s/'$OLD_STRING'/'$NEW_STRING'/g" jboss-beans.xml

But then the variables are interpreted before calling sed and the again contain special characters:

$ echo  "s/'$OLD_STRING'/'$NEW_STRING'/g"
s/'<property name="webServiceHost">${jboss.bind.address}</property>'/'<!--<property name="webServiceHost">${jboss.bind.address}</property>-->'/g

For that reason sed has this special featur allowing to define the s/// command delimiters by simply using them, e.g.:

sed -i "s#'$OLD_STRING'#'$NEW_STRING'#g" jboss-beans.xml

Still your search expression contains special regexp characters, and using sed like this is just waste of its abilities. I would write the expression like this:

sed -i 's/\(<.*webServiceHost.*jboss.bind.address.*>\)/<!--\1-->/' jboss-beans.xml

Of course you can make the match string more or less specific according to your needs. There is also other nice feature that can help. sed allows to narrow editing operations to the lines matching a specific pattern. Your command could look like this:

sed -i '/webServiceHost/ s/^\(.*\)$/<!--\1-->/' jboss-beans.xml
Related Question