2

I am trying to replicate a workplace problem. I have a xml file like below

[~]$ less -N sample.xml
  1     <SOURCE BUSINESSNAME ="" NAME ="TABLE1" FOO="ABCD"..... >
  2         <SOURCEFIELD BUSINESSNAME ="" NAME ="COL_XYZ" />
  3         <SOURCEFIELD BUSINESSNAME ="" NAME ="COL_ABCD" />
  4         ...
  5                 ...
  6     </SOURCE>
  7     <SOURCE BUSINESSNAME ="" NAME ="TABLE2" ....... >
  8             <SOURCEFIELD BUSINESSNAME ="" NAME ="COL_ABCD" />
  9         <SOURCEFIELD BUSINESSNAME ="" NAME ="COL_XYZABC" />    
 10         ...
 11                 ...
 12     </SOURCE>
 13         <SOURCE BUSINESSNAME ="" NAME ="TABLE3" .... >
 14         <SOURCEFIELD BUSINESSNAME ="" NAME ="COL_PQR" />
 15         <SOURCEFIELD BUSINESSNAME ="" NAME ="COL_ABCD" />
 16         ...
 17                 ...
 18     </SOURCE>

Now I want the value of NAME field where any of the SOURCEFIELD NAME is like XYZ.

For example, in the given example I need TABLE1 as line 2 contains COL_XYZ. And also TABLE2 as in line 9 we have COL_XYZABC

I was thinking some way, to get row 1,2,7,9,13 as output and then grep -B1 XYZ|grep -w SOURCE field to get only row 1,7 in output.

Expected Output:
TABLE1
TABLE2

What I tried so far

  • Doing a grep on SOURCE is not working as every row has at least one of them.
  • Doing a egrep -w "SOURCE|XYZ" is not working as I need XYZABC will not satisfy its condition.

Could someone please suggest something which I can try to get desired result. I am using Linux 2.6.18-371.el5

1
  • 1
    @mikeserv In the example above, TABLE3 dont have a matching XYZ name (in line 14 and 15). So I don't need TABLE3 in output. The answer by RobertL worked like a charm. I also came up with another answer which is slower than that but works. Thanks for giving it a thought anyways. Cheers.
    – Utsav
    Dec 14, 2015 at 12:59

3 Answers 3

2

You can accomplish this using the hold space feature of sed.

Run sed with the -n option to suppress the automatic printing of input lines.

When the line containing <SOURCE is seen, save the value of the NAME attribute in sed's hold space.

When the <SOURCEFIELD line containing XYZ is seen, print the contents of the hold space.

#!/bin/sh

sed -n '
    /<SOURCE / {              # execute block {} on lines matching "<SOURCE "
        s/.* NAME *="//       # remove everything upto NAME attribute value
        s/".*//               # remove everything after attribute value
        h                     # copy pattern space to the hold space
    }
    /<SOURCEFIELD.*XYZ/ {     # SOURCEFIELD contains XYZ, execute {} block
        g                     # copy hold space to pattern space
        p                     # print
    }
' "$@"
1
  • I was also able to achieve this by this command but it is slower egrep -w ""\<SOURCE"|"SOURCEFIELD.*XYZ.*"" sample.xml|grep -B1 XYZ|grep -w SOURCE|awk -F" NAME =" '{print $2}'|awk '{print $1}'|sed 's/"//g'
    – Utsav
    Dec 14, 2015 at 12:47
1
sed -netP -eH            -e'# Hold every line and test for s///uccess' \
    -e'\|<[^F]*[ >]|!d'  -e'# if < then F before [ >]: delete'         \
-ex -e'\|_XYZ[^_]*>|!d'  -e'# first exchange buffers; if !XYZ: delete' \
    -e's|[^"]*|\n&\n|4'  -e'# wrap 4th no quotes series in newlines'   \
    -e'D;:P' -eP         -e'# Delete up to first newline, :Print if true'

TABLE1
TABLE2

...and when I add an XYZ to the last field in the third list, TABLE3 prints, too...

0
0

Thanks to RobertL for this answer.

I also came up with a combination of grep/awk/sed to achieve the same but it is slower obviously. Just wanted to put it out there.

egrep -w ""\<SOURCE"|"SOURCEFIELD.*XYZ.*"" sample.xml|grep -B1 XYZ|grep -w SOURCE|\                                                              
> awk -F" NAME =" '{print $2}'|awk '{print $1}'|sed 's/"//g'

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .