I am writting a shell script to delete an entry from a specific group. eg: file name is dest.xml

<domain id="1" group_name="group1">
    <node id="ABC">
    <node id="PQR">
    <node id="XYZ">
<domain id="2" group_name="group2">
    <node id="PQR">
    <node id="XYZ">
<domain id="3" group_name="group3">
    <node id="ABC">
    <node id="PQR">

I want to delete an entry from above file (filename is dest.xml) with node id="PQR" from group_name="group1" (it should not get deleted from group2 and group3). I can do it by reading a file sequentially n then delete it from a specific group. But if the file is to large (>10k lines) then it takes time.

Is there any simple way to this this?

  • Sure, but better gives a known file type, that one is unknown. – Gilles Quenot Feb 20 '13 at 14:50
  • 1
    That is not valid XML. – jordanm Feb 20 '13 at 15:26
  • @jordanm its probably a snippet and it doesn't matter for the question of the OP. – Silverrocker Feb 20 '13 at 15:42
  • 2
    Is this XML? If so, use an XML parser. If this is some ad-hoc vaguely XML-like syntax, awk as proposed by Stephane is fine. – Gilles Feb 21 '13 at 0:42

I would use awk if the format doesn't diverge from the sample you provided:

awk -F'[<>="[:blank:]]+' '
  $2 == "domain" {group = $(NF-1)}
  !(group == "group1" && $2 == "node" && $(NF-1) == "PQR")
  ' < dest.xml > new-dest.xml

to remove that "PQR" node in the "group1" domain.

$ diff -u dest.xml new-dest.xml
--- dest.xml    2013-02-22 07:01:48.732227421 +0000
+++ new-dest.xml        2013-02-22 07:02:16.111512820 +0000
@@ -1,6 +1,5 @@
 <domain id="1" group_name="group1">
     <node id="ABC">
-    <node id="PQR">
     <node id="XYZ">
 <domain id="2" group_name="group2">

If you mean you wish to remove that node from the XML file in place, then that's not really possible. You'd need at least to rewrite the part that is after that node to shift the data as many bytes backward.

Alternatively, you could replace that node with blanks which would mean you could get away with replacing those bytes only.

perl -ne '
  if (/<domain.*group_name="(.*?)"/) {
    $in = $1 eq "group1"
  } elsif ($in && /<node id="PQR"/) {
    s/./ /g;
    seek STDOUT,tell(STDIN)-length$_,0;
  }' < dest.xml 1<> dest.xml

Add a ;exit after the print above if there's only one such node and you want to stop processing as soon as you've found it.

  • Hi Stephane, I am new to unix shell scripts. I tried running this command but its not working for me. Can you please interpret the above command which is missing my file name "dest". – Pravin Nagare Feb 21 '13 at 7:37
  • @PravinNagare, just append your file name to this command line (separated by a space of course). – LiuYan 刘研 Feb 21 '13 at 8:04
  • Hi, I appended file name after command seperated by space awk -F'[<>="[:blank:]]+' ' $2 == "domain" {group = $(NF-1)} !(group == "group1" && $2 == "node" && $(NF-1) == "PQR")' dest.xml but its not working for me. Its just displaying the whole file as it is. – Pravin Nagare Feb 22 '13 at 6:48
  • @PravinNagare, You mean it's not removing the PQR node in the first domain? – Stéphane Chazelas Feb 22 '13 at 7:04
  • @StephaneChazelas yes, Its not removing PQR node from group1. – Pravin Nagare Feb 22 '13 at 7:14

I wrote a quick python script, do not know if it's simple enough.

Place this script in the same folder with your dest.xml.

import re
FILENAME = 'dest.xml'
GROUPNAME = 'group1'

with open(FILENAME) as f:
    in_group = False
    for line in f:
        line = line.strip()
        group_pattern = 'group_name="{0}">'.format(GROUPNAME)
        end_group_pattern = '</domain>'
        node_pattern = '<node id="{0}">'.format(NODEID)
        if re.search(group_pattern, line):
            in_group = True
        if re.search(end_group_pattern, line):
            in_group = False
        if re.search(node_pattern, line) and in_group:
            print line

And now is the awk version.

#!/usr/bin/awk -f
    GROUPNAME = "group1"
    NODEID = "PQR"
    in_group = 0
    group_pattern =  ".*group_name=\"" GROUPNAME "\""
    end_group_pattern = "</domain>"
    node_pattern = "<node id=\"" NODEID "\">"
$0 ~ group_pattern {
   in_group = 1
$0 !~ node_pattern || in_group == 0 {
    print $0
$0 ~ end_group_pattern {
    in_group = 0

Run this awk script with argument is your file name dest.xml. Seem that it's simpler than the python version.

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.