Compare 2 files on specific row only

Viewed 64

I need to compare 2 files and find the matching rows. The only problem is that I need to check the 4th row out of 5 from DocumentList file and return the entire line if a match is found in final file.

cat DocumentList.xml
<?xml version="1.0" encoding="UTF-8" ?> <block-list:block-list xmlns:block-list="http://openoffice.org/2001/block-list">
<block-list:block block-list:abbreviated-name="adn" block-list:name="and" />
<block-list:block block-list:abbreviated-name="tesst" block-list:name="test" />
<block-list:block block-list:abbreviated-name="tust" block-list:name="test" />
<block-list:block block-list:abbreviated-name="seme" block-list:name="same"/>

And the second file is:

cat final.txt
and
test
india

I can extract the forth row using this command, but do not know how to compare with the lines from final file

awk -F '\"' '{print $4}' DocumentList.xml

Expected Result:

<block-list:block block-list:abbreviated-name="adn" block-list:name="and" />
<block-list:block block-list:abbreviated-name="tesst" block-list:name="test" />
<block-list:block block-list:abbreviated-name="tust" block-list:name="test" />

I have also tried something like this, but it does not return the entire line from DocumetList file.

awk -F '\"' 'FNR==NR {a[$4]; next} $1 in a'  DocumentList.xml final.txt

final.txt file is 1 GB, DocumentList is 25 MB and both have unicode characters.

3 Answers

Just swap the order of reading files:

awk -F '\"' 'FNR==NR {a[$0]; next} $4 in a' final.txt DocumentList.xml

Output:

<block-list:block block-list:abbreviated-name="adn" block-list:name="and" />
<block-list:block block-list:abbreviated-name="tesst" block-list:name="test" />
<block-list:block block-list:abbreviated-name="tust" block-list:name="test" />

With your shown samples, please try following awk code. Written and tested in GNU awk.

awk '
FNR==NR{
  arr1[$0]
  next
}
match($0,/block-list:name="([^"]*)"/,arr2) && (arr2[1] in arr1)
' final.txt DocumentList.xml

Explanation: In awk program reading both the Input_file(s) named final.txt and DocumentList.xml. Then in main program using condition FNR==NR which will be TRUE when final.txt is being read. In that condition block I am creating an array named arr1 whose index is current line and then using next will skip all further statements from there onwards. Then I have used match function of awk, which matches regex mentioned in it(block-list:name="([^"]*)") this matches everything from block-list:name= followed by a " till next occurrence of " comes keep in mind () creates values and stores them into array named arr2 which we will access later on. Then using && (arr2[1] in arr1) condition to check if value of arr2's 1st element comes in array arr1 then print that line(basically matching values of final.txt and needed value from DocumentList.xml).

you can try,

search=$(awk 'BEGIN{OFS=ORS=""}{if(NR>1){print "|"}print $1;}' final.txt)
# store 'and|test|india' in search variable

grep -E "block-list:name=\"($search)\"" DocumentList.xml

you get,

<block-list:block block-list:abbreviated-name="adn" block-list:name="and" />
<block-list:block block-list:abbreviated-name="tesst" block-list:name="test" />
<block-list:block block-list:abbreviated-name="tust" block-list:name="test" />

Or using, 'awk'

awk 'BEGIN{FS="block-list:name=\""}
     FNR==NR {a[$1]; next} {f=$2;gsub(/".*/,"",f)} 
     FNR>1 && f in a{print $0}
' final.txt DocumentList.xml

Note: for xml files, I don't recommend how you are doing it, is better to use xml-parser

Related