0

I'm trying to update a line in an xml file using a custom shell function and sed

In cmd line, I run it as follow: updateloc db_name

However it does not update anything. Below sample of the code

updateloc(){
 db_name=$1
 file="file.xml"
 olddb="<dbname><![CDATA[olddb]]></dbname>"
 newddb="<dbname><![CDATA[$db_name]]></dbname>"
 sed -i '' 's/$olddb/$newdb/g' $file

}

Turn
  • 6,656
  • 32
  • 41
Nicc
  • 777
  • 3
  • 8
  • 19

2 Answers2

1

The right tool for this job is XMLStarlet. To modify any element named dbname with the new value:

updateloc() {
  local db_name=$1 file=file.xml
  xmlstarlet ed --inplace -u '//dbname' -v "$db_name" "$file"
}

To replace only elements with the old value olddb:

updateloc() {
  local db_name=$1 file=file.xml
  xmlstarlet ed --inplace \
    -u "//dbname[. = 'olddb']" -v "$db_name" "$file"
}

Note that while the serialization generated by XMLStarlet won't necessarily use CDATA, it is guaranteed to be semantically equivalent, and to behave the precise same way in any XML-compliant parser.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • indeed it works as well as solution proposed below. But one more thing is that the "olddb" is a dynamic value which can be any string (only letters)...how would i introduce regex in this syntax ? – Nicc Dec 16 '15 at 13:46
  • To be clear: You have multiple database definitions in the same config file, and you need to change only a specific one -- and all of them besides that one have names that don't match your regex? (If you only have one database definition, why can't you use the first form?) – Charles Duffy Dec 16 '15 at 15:09
  • If it's a matter of the `dbname` element name being used for different things in different contexts, the best-practice way to filter is not on content but on context -- on *where in the config file* the one you want to change is, ie. what its parent and grandparent elements are. – Charles Duffy Dec 16 '15 at 15:10
  • (XQuery 2.0 and 3.0 have proper regex support, but 1.0 [provided by libxml, the library used by XMLStarlet] does not. There are some extensions available, but I'd need to do some research to see if they're actually available/enabled in this context). – Charles Duffy Dec 16 '15 at 15:13
  • ...the other best-practice approach being having a static `id` attribute on any element you expect to need to programatically address, at which point you could refer to `"//dbname[@id='whichever']"` without worrying about what its content is at the moment. You'll see folks doing web work do the same thing, adding unique IDs to elements they want to access from their javascript. – Charles Duffy Dec 16 '15 at 15:16
0

1) $olddb and $newdb are not getting expanded because they're in single quotes

2) Your sed command is getting tripped up by all those xml characters - '[' and '/' are both meaningful to sed. You'd have to escape all those, and perhaps use a different regex delimiter (e.g. 's#$olddb#$newdb#g'). It's probably a bad idea to use sed for this, unless the format of file.xml is very consistent (the closing tag could be on a separate line for example).

That said, this would work for your example:

olddb='<dbname><!\[CDATA\[olddb\]\]></dbname>'
newdb='<dbname><!\[CDATA\[newdb\]\]></dbname>'
sed -i '' "s#$olddb#$newdb#g" $file

Grep and Sed Equivalent for XML Command Line Processing has some good approaches for better ways to mangle xml from the command line.

Ian McGowan
  • 3,461
  • 3
  • 18
  • 23
  • indeed it works. one more thing is that the "olddb" in $olddb is a dynamic value which can be any string (only letters)...how would i introduce regex in this syntax ? – Nicc Dec 16 '15 at 13:45