I have the XML like this:

<string name="foo-bar">"bar-bar -bar"</string>
<string name="asdf_qwe-rty" translatable="false">"Lorem ipsum"</string>

Now, the name attribute can't contain - character, so I'd like to replace it with _.

<string name="foo_bar">"bar-bar -bar"</string>
<string name="asdf_qwe_rty" translatable="false">"Lorem ipsum"</string>

How can I do this, preferably with sed?


Assuming that the XML document is well formed, you may use xmlstarlet to replace all occurrences of the character - in the name attributes of all string nodes with _ like so:

$ xmlstarlet ed -u '//string/@name' -x 'translate(.,"-","_")' file.xml
<?xml version="1.0"?>
  <string name="foo_bar">"bar-bar -bar"</string>
  <string name="asdf_qwe_rty" translatable="false">"Lorem ipsum"</string>
  <string name="_test1_">name="-test1-"</string>
  <!-- <string name="-test2-">name="-test2-"</string> -->

(I've added a wrapping root node here to make the document well formed, and a couple of extra cases to show that we won't affect the node values, or contents of comments).

The xmlstarlet expression will find all the relevant attributes using the XPath expression //string/@node, and apply a simple translation of each - into _ in each of these attribute's value. The output is written to standard output.


Any solution based on sed (or anything else that doesn't do proper XML parsing) will be wrong in a proportion of edge cases. For example, the solution from @Pitel:

(a) replaces the content of things that look like name attributes even if they are in comments or text rather than in a start tag

(b) also changes the content of an attribute named firstname or lastname

(c) fails to spot the attribute if it has whitespace around the equals sign.

So it might be adequate for one-off ad-hoc use, but don't put it into a production workflow. If you need something of production quality, use an XSLT transformation. It's not difficult:

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0">
  <xsl:mode on-no-match="shallow-copy"/>
  <xsl:template match="@name">
    <xsl:attribute name="name" select="translate(., '-', '_')"/>
  • That looks very promising; you may want to explain (for the sake of the "non-XML-Gurus") how to apply this transformation to the XML file in question.
    – AdminBee
    Jan 14 '20 at 11:16
sed -r ':a; s/(name="[^-"]*)-/\1_/; ta'
  • Congratulations on solving the problem youself. You might consider explaining what your sed command does, so that others may learn more easily from your answer.
    – AdminBee
    Jan 14 '20 at 10:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.