ajay_t
ajay_t

Reputation: 2385

Adding XML element in XML file using sed command in shell script

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?

Upvotes: 10

Views: 31791

Answers (5)

Reino
Reino

Reputation: 3443

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>

Upvotes: 1

Henning
Henning

Reputation: 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
}

Upvotes: -1

Farvardin
Farvardin

Reputation: 5424

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

Upvotes: 9

potong
potong

Reputation: 58548

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

Upvotes: 2

jaypal singh
jaypal singh

Reputation: 77175

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.

Upvotes: 6

Related Questions