# How to extract first line that matches pattern from a file and if pattern doesn't exist print fixed output?

I have a text file shown below:

Doc_A 123 abc
Doc_A 456 def
Doc_A 789 ghi
Doc_B 123 abc
Doc_B 456 def
Doc_C 123 abc
Doc_C 456 def
Doc_C 789 ghi
Doc_C 101 jkl


And a reference file

Doc_A
Doc_B
Doc_C
Doc_D
Doc_E
Doc_F


I want to extract the first line from text file that matches the name in the reference file and print that line and if there is no match print a certain fixed pattern as shown:

Doc_A 123 abc
Doc_B 123 abc
Doc_C 123 abc
Doc_D 10 20
Doc_E 10 20
Doc_F 10 20


I can use awk as shown below to print for matching pattern. How would I print patterns not found, certain fixed way as I require?

awk 'FNR == NR { a[$1] = 0; } FNR != NR { for (i in a) if ($0 ~ i && a[i]++ == 0) { print $0; break; } }' \ref.txt file.txt  ## 3 Answers How about deleting the entries from the lookup array as you match them, and then printing what's left at the end? $ awk 'NR==FNR {a[$1]; next}$1 in a {print; delete a[$1]} END {for (i in a) print i, "10 20"} ' ref.txt file.txt Doc_A 123 abc Doc_B 123 abc Doc_C 123 abc Doc_D 10 20 Doc_E 10 20 Doc_F 10 20  (Note that awk doesn't guarantee the order of array traversal - if that's an issue.) # Explanation While NR==FNR, we are processing the first named file (ref.txt): we create an array entry with its first (in this case, only) field as its index and then move to the next record. We don't need to assign a value to the array element. Otherwise, we are processing the second named file (file.txt). We check whether its first column has a match in the array a that we constructed from the reference file, and print the record $0 if it does. Then we delete the entry.

Deleting serves two purposes: it "uniquifies" the match, because next time we test $1 in a for the same $1, the answer will be false. It also means that after all lines of file.txt have been processed, any remaining elements in a have not been matched - we can print these in your "fixed" format in an END block.

• array traversal is not an issue. Thanks – rkatraga Apr 17 at 18:43
• Can you explain in detail, what exactly is happening here? I am unable to understand it fully. – rkatraga Apr 17 at 21:45
• @rkatraga I have added some explanation – steeldriver Apr 17 at 21:54
• This is great, I am just so amused by awk. – rkatraga Apr 17 at 22:51

Is awk a requirement for your task? grep could also be used.

Your files appear to be space-delimited. The solution below rests on the assumption that the fixed patterns in your reference file will never contain whitespace.

Let the text file be file.txt. Let the reference file be ref.txt.

$for P in$(cat ref.txt); do grep -m1 "^$P[[:blank:]]" file.txt || printf "%s 10 20\n" "$P"; done
Doc_A 123 abc
Doc_B 123 abc
Doc_C 123 abc
Doc_D 10 20
Doc_E 10 20
Doc_F 10 20

• What is white space? can you elaborate. – rkatraga Apr 17 at 19:51
• So you mean its assuming there is no whitespace before pattern? Like in this case before Doc_A? Also why is [[:blank:]] used after ^$P? what purpose does tab and space serve here? to recognize diff between Doc_A & Doc_AA? Can you clarify? Because I didn't think about it until I saw the code. Thanks in advance, I am very new, and hopefully soon enough will pick up. – rkatraga Apr 17 at 20:03 • No, I mean no whitespace in ref.txt. And yes, you are correct that the [[:blank:]] means that we require that there must be whitespace in file.txt after the pattern match, to avoid having Doc_A match Doc_AA. – Jim L. Apr 17 at 20:30 You can do it multiple ways, like as for instance : awk ' NR == FNR && !($1 in a){a[$1]=$0}
NR != FNR{print ($1 in a) ? a[$1] : $1" 10 20"} ' inp ref.txt perl -lane '$h{$F[0]} =$_ unless exists $h{$F[0]}}{
while ( <STDIN> ) {
chomp;
print(exists $h{$_} ? $h{$_} : qq<$_ 10 20>); } ' inp < ref.txt while IFS= read -r a do grep -m1 -F -- "$a" inp || echo "\$a 10 20"
done < ref.txt


Results:

Doc_A 123 abc
Doc_B 123 abc
Doc_C 123 abc
Doc_D 10 20
Doc_E 10 20
Doc_F 10 20