10

I am using sed command to insert an xml element into the existing xml file.

I have xml file as

<Students>
    <student>
        <name>john</>
        <id>123</id>
    </student>
    <student>
        <name>mike</name>
        <id>234</id>
    </student>
</Students>

I want to add new elememt as

    <student>
        <name>NewName</name>
        <id>NewID</id>
    </student>

So my new xml file will be

<Students>
    <student>
        <name>john</>
        <id>123</id>
    </student>
    <student>
        <name>mike</name>
        <id>234</id>
    </student>
    <student>
        <name>NewName</name>
        <id>NewID</id>
    </student>
</Students>

For this I have written shell script as

#! /bin/bash

CONTENT="<student>
            <name>NewName</name>
            <id>NewID</id>
        </student>"

#sed -i.bak '/<\/Students>/ i \ "$CONTENT" /root/1.xml
sed -i.bak '/<\/Students>/ i \'$CONTENT'/' /root/1.xml

I am getting error as

sed: can't read <name>NewName</name>: No such file or directory
sed: can't read <id>NewID</id>: No such file or directory
sed: can't read </student>: No such file or directory

And in the xml file, only <student> is added. The remaining elements are not added. Does anyone know why this error?

2
  • 4
    Several tools exist to manipulate XML files. Use those instead of regular expressions. Commented Apr 23, 2014 at 4:37
  • @devnull would be nice of you to list one (or two) of those tools Commented Sep 4, 2017 at 6:13

5 Answers 5

9

change this:

CONTENT="<student>
            <name>NewName</name>
            <id>NewID</id>
        </student>"

to this:

CONTENT="<student>\n<name>NewName</name>\n<id>NewID</id>\n</student>"

and then:

C=$(echo $CONTENT | sed 's/\//\\\//g')
sed "/<\/Students>/ s/.*/${C}\n&/" file
Sign up to request clarification or add additional context in comments.

2 Comments

If my tag is <name>New Name</name>, then how can handle whitespace here? '\t' will add one tab and I need single whitespace
@USer007 You can escape the space character with backslash-space
6

You cannot have an unescaped newline in sed replacement text, that is $CONTENT in your example. sed uses the newline just like the shell does, to terminate a command.

If you need a newline in the replacement text, you need to precede it with a backslash.

There is another way to add text using the r option. For example:

Lets say your main file is;

$ cat file
<Students>
    <student>
        <name>john</>
        <id>123</id>
    </student>
    <student>
        <name>mike</name>
        <id>234</id>
    </student>
</Students>

You text you want to add is in another file (not variable):

$ cat add.txt
    <student>
        <name>NewName</name>
        <id>NewID</id>
    </student>

You can do (using gnu sed):

$ sed '/<\/Students>/{ 
    r add.txt
    a \</Students>
    d 
}' file
<Students>
    <student>
        <name>john</>
        <id>123</id>
    </student>
    <student>
        <name>mike</name>
        <id>234</id>
    </student>
    <student>
        <name>NewName</name>
        <id>NewID</id>
    </student>
</Students>

However, having given this option, it is still a very bad idea to parse xml with regular expression. It makes the solution very fragile and easy to break. Consider this as a learning exercise only.

Comments

2

This might work for you (GNU sed & Bash):

CONTENT='    <student>\
    <name>NewName</name>\
    <id>NewID</id>\
</student>'

sed '/<\/Students>/i\'"$CONTENT" file

Alternatively, put the new students in a file and:

sed '/<\/Students>/e cat new_student_file' file

2 Comments

Here if tag is like <name>New Name</name>, then how can I handle this whitespace.
@Optimus for the variable just remember that the last character of each line (but the last) must end in `\`. For the file solution just write the text as you see it.
1

Please DO NOT use sed to parse/edit XML! Use an XML-parser, like and , instead.

Xidel

With "direct element constructors" (and Xidel's own x:replace-nodes()):

$ xidel -s "input.xml" -e '
  x:replace-nodes(
    Students/student[last()],
    function($x){
      $x,
      <student><name>NewName</name><id>NewID</id></student>
    }
  )
' --output-node-format=xml --output-node-indent

With "computed constructors":

$ xidel -s "input.xml" -e '
  x:replace-nodes(
    Students/student[last()],
    function($x){
      $x,
      element student {
        element name {"NewName"},
        element id {"NewID"}
      }
    }
  )
' --output-node-format=xml --output-node-indent

XMLStarlet

$ xmlstarlet ed -O \
  -a 'Students/student[last()]' -t elem -n 'student' \
  -s 'Students/student[last()]' -t elem -n 'name' -v 'NewName' \
  -s 'Students/student[last()]' -t elem -n 'id' -v 'NewID' \
  "input.xml"

Output in all 3 cases:

<Students>
  <student>
    <name>john</name>
    <id>123</id>
  </student>
  <student>
    <name>mike</name>
    <id>234</id>
  </student>
  <student>
    <name>NewName</name>
    <id>NewID</id>
  </student>
</Students>

Comments

-1

My goal was to inject xml snippets from another file into pom.xml and the following code works for me. It also ensures that \t (tabs) will be taken into the new file, so it will be formated in the correct way.

function inject_plugin_into_pom {
  local plugin_file=$1
  local pom_file=$2

  # read from file into array
  declare -a xml_array
  while IFS= read -r line; do
    xml_array+=("$line")
  done < "$plugin_file"

  # inject into xml line by line
  for element in "${xml_array[@]}"; do
    # echo -e             | takes care of the \t
    # sed 's/\//\\\//g'   | replaces / with \/
    escaped=$(echo -e "$element" | sed 's/\//\\\//g')
    sed -i -e "s/<\/plugins>/\t${escaped}\n\t\t<\/plugins>/g" $pom_file
  done
}

1 Comment

Welcome to StackOverflow, and thank you for your contribution. However, while this seems superficially related to the question, it does not address the problem OP described, so it's not really an answer to this question.

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.