What you could do to compare lines, rather than words, is a little contorted, but:
while read -r -u 3 line1 && read -r -u 4 line2
do
if [ "$line1" = "$line2" ]
then echo "$line2"
fi
done 3<filea.txt 4<fileb.txt
I've explicitly used file descriptors 3 and 4 for symmetry (Polya: "Try to treat symmetrically what is symmetrical, and do not destroy wantonly any natural symmetry”); it would also be possible but less clear to write:
while read -r line1 && read -r -u 3 line2
do
if [ "$line1" = "$line2" ]
then echo "$line2"
fi
done <filea.txt 3<fileb.txt
What does read -u stand for, and what do 3 and 4 stand for?
Read the Fine Manual — the bash
manual is available online, and if you haven't already got it bookmarked, now would be a fine time to bookmark it.
The -u
fd
option means 'read from the designated file descriptor (3 or 4 in the example code) instead of from standard input (which is file descriptor 0, of course). The 3 and 4 stand for the file descriptors to read from. The code redirects file descriptor 3 from filea.txt
and file descriptor 4 from fileb.txt
for the duration of the while
loop. Any process that reads file descriptor 3 will get data from filea.txt
; ditto 4. The script directs one read
command to read from 3 and the other from 4, and read
reads lines, so the net effect is that while there is data available from both filea.txt
and fileb.txt
, the loop will run, comparing the two lines and printing.
If your objective is to get the lines that are common between two files printed, then using the comm
command is much better — see Jonas Elfström's answer for a good way to do that.
Assuming your objective is to read lines from two files in parallel, I can't immediately think of another way to do this in bash
. If you need words from two files in parallel (which is what the for i in $(<filea.txt)
notation would give), you'd probably need to use process substitution in conjunction with the I/O redirection:
while read -r -u 3 word1 && read -r -u 4 word2
do
if [ "$word1" = "$word2" ]
then echo "$word2"
fi
done 3< <(printf "%s\n" $(<filea.txt)) 4< <(printf "%s\n" $(<fileb.txt))
There are other ways to achieve that too, notably using tr
instead of printf
, which would be better if the files are big (many megabytes big).