1

I have a tag

<string name="currencysym">$</string> 

in string.xml And what to change the $ symbol dynamically. Use the below command but didn't work:

currencysym=&#x20B9;

sed -i '' 's|<string name="currencysym">\(.*\)<\/string>|<string name="currencysym">'"<!\[CDATA\[${currencysym}\]\]>"'<\/string>|g'

Getting OUTPUT:

<string name="currencysym"><![CDATA[<string name="currencysym">$</string>#x20B9;]]></string>

" & " Has Removed...

But I need:

<string name="currencysym"><![CDATA[&#x20B9;]]></string>
Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
Rawan-25
  • 1,753
  • 1
  • 17
  • 25

3 Answers3

3
  1. using xml-parser/tool to handle xml is first choice
  2. instead of <..>\(.*\)<..> better use <..>\([^<]*\)<..> in case you have many tags in one line
  3. & in replacement has special meaning, it indicates the whole match (\0) of the pattern. That's why you see <...>..</..> came to your output. If you want it to be literal, you should escape it -> \&
Kent
  • 189,393
  • 32
  • 233
  • 301
2

First problem is the line

currencysym=&#x20B9;

This actually reads as "assign empty to currencysym and start no process in the background":

  • In bash you can set an environment variable (or variables) just or one run of a process by doing VAR=value command. This is how currencysym= is being interpreted.
  • The & symbol means start process in the background, except there is no command specified, so nothing happens.
  • Everything after # is interpreted as a comment, so #x20B9; is just whitespace from Bash's point of view.
  • Also, ; is a command separator, like &, which means "run in foreground". It is not used here because it is commented out by #.

You have to either escape &, # and ;, or just put your string into single quotes: currencysym=\&\#x20B9\; or currencysym='&#x20B9;'.

Now on top of that, & has a special meaning in sed, so you will need to escape it before using it in the sed command. You can do this directly in the definition like currencysym=\\\&\#x20B9\; or currencysym='\&#x20B9;', or you can do it in your call to sed using builtin bash functionality. Instead of accessing ${currencysym}, reference ${currencysym/&/\&}.

You should use double-quotes around variables in your sed command to ensure that your environment variables are expanded, but you should not double-quote exclamation marks without escaping them.

Finally, you do not need to capture the original currency symbol since you are going to replace it. You should make your pattern more specific though since the * quantifier is greedy and will go to the last closing tag on the line if there is more than one:

sed 's|<string name="currencysym">[^<]*</string>|<string name="currencysym"><![CDATA['"${currencysym/&/\&}"']]></string>|' test.xml

Yields

<string name="currencysym"><![CDATA[&#x20B9;]]></string>

EDIT

As @fedorqui points out, you can use this example to show off correct use of capture groups. You could capture the parts that you want to repeat exactly (the tags), and place them back into the output as-is:

sed 's|\(<string name="currencysym">\)[^<]*\(</string>\)|\1<![CDATA['"${currencysym/&/\&}"']]>\2|' test.xml
Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
  • For Mac : sed -i '' 's|[^<]*|<![CDATA['"${currencysym/&/\&}"']]>|' strings.xml – Rawan-25 Dec 22 '16 at 05:59
  • Good one! Instead of printing `` back, you can capture it and print back. – fedorqui Dec 22 '16 at 12:23
  • @Rawan-25. I am not sure I see the difference on the Mac. – Mad Physicist Dec 22 '16 at 16:07
  • @Rawan-25. Glad to hear it. Check out [this post](http://stackoverflow.com/q/8577060/2988730) for more info BTW. In general, XML+regex is not a good idea, although for simple cases like yours it's the quickest and easiest thing to do. – Mad Physicist Dec 23 '16 at 15:57
1
sed -i '' 's|\(<string name="currencysym">\)[^<]*<|\1<![CDATA[\&#x20B9;]]><|g' YourFile
  • the group you keep in buffer is the wrong one in your code, i keep first part not the &
  • grouping a .* is not the same as all untl first < you need. especially with the goption meaning several occurence could occur and in this case everithing between first string name and last is the middle part (your group).
  • carrefull with & alone (not escaped) that mean 'whole search pattern find' in replacement part
NeronLeVelu
  • 9,908
  • 1
  • 23
  • 43