Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

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>
<domain id="2" group_name="group2">
    <node id="PQR">
    <node id="XYZ">
</domain>
<domain id="3" group_name="group3">
    <node id="ABC">
    <node id="PQR">
 </domain>

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?

share|improve this question
    
Sure, but better gives a known file type, that one is unknown. –  sputnick 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

2 Answers 2

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.

#!/usr/bin/python
import re
FILENAME = 'dest.xml'
GROUPNAME = 'group1'
NODEID = 'PQR'

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:
            pass
        else:
            print line

And now is the awk version.

#!/usr/bin/awk -f
BEGIN {
    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.

share|improve this answer

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>
 <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;
    print
  }' < 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.

share|improve this answer
    
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

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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