0

I try to replace string in XML file with sed, but since its regular expression doesn't support non-greedy expressions, I encounter a problem.

XML Example:

<opt>
  <Node active="yes" file="/home/user/random_filename" last_time="17/07/14-00:02:07" time_in_min="5" />
</opt>

I want to find the file attribute, which is random string, and replace it with another string.

This command replaces the string, BUT removes the trailing data.

sed 's/file=".*"/file="new_file_name"/' file.xml

Output:

<opt>
   <Node active="yes" file="new_file_name" />
</opt>

How should I handle it?

1

3 Answers 3

2

Use "[^"]*" instead of ".*":

Input:

<opt>
  <Node active="yes" file="/home/user/random_filename" last_time="17/07/14-00:02:07" time_in_min="5" />
</opt>

Command:

sed 's/file="[^"]*"/file="new_file_name"/'

Output:

<opt>
  <Node active="yes" file="new_file_name" last_time="17/07/14-00:02:07" time_in_min="5" />
</opt>
Sign up to request clarification or add additional context in comments.

Comments

1

This doesn't use sed but when manipulating xml documents, you might want to consider xslt. It may be an over the top solution to your problem, but it's a great fit for working with xml.

filter.xsl:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" omit-xml-declaration="yes"/>

<!-- The replacement string is passed as a parameter.
     You can have as many param elements as you need. -->
<xsl:param name="NEW_NAME"/>

<!-- Copy all attributes and nodes... -->
<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

<!-- ...but when you encounter an element with a file attribute,
     replace it with the value passed in the parameter named $NEW_NAME. -->
<xsl:template match="@file">
    <xsl:attribute name="file">
        <xsl:value-of select="$NEW_NAME"/>
    </xsl:attribute>
</xsl:template>

</xsl:stylesheet>

A libxslt example (add as many --stringparam name/value pairs that you need):

xsltproc --stringparam NEW_NAME new_file_name filter.xsl file.xml

Result:

<opt>
    <Node active="yes" file="new_file_name" last_time="17/07/14-00:02:07" time_in_min="5"/>
</opt>

2 Comments

Nice one! I will consider it. I need to change more than one argument and it looks like most convenient way. Thanks.
The match attribute xpath expression can be made more specific if needed. For example to only change file attributes in Node elements, change match="@file" to match="Node/@file". You can also match based on the contents of the file attribute. To change only those that start with '/home/user/' use match="Node/@file[starts-with(., '/home/user/')]".
0
$ sed 's/\(.*file="\)[^"]*/\1new_file_name/' file
<opt>
  <Node active="yes" file="new_file_name" last_time="17/07/14-00:02:07" time_in_min="5" />
</opt>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.