Compare two files and print matches – large files

awkgreptext processing

I need to compare 2 files and print matched lines.
If file1 username is in file2 (field 1) I want to print it to new matched file.

File1.txt:

Hey123
Johnson
Hanny123
Fanny

(file1 is 240MB – 20.000.000 lines)

File2.txt:

Gromy123:hannibal
Hey123:groll
Hanny123:tronda9
Kroppsk:football23

(file2 is 1.4GB – 69.000.000 lines)

Expected matched lines output:

Hanny123:tronda9
Hey123:groll

I have been trying for 4 hours without success. Both the files are sorted and I have tried join + countless of grep / awk commands. My big problem is RAM exhaustion. Would love some help how I could approach this, so large files.

Best Answer

If the files are sorted (the samples you posted are) then it's as simple as

join -t : File1.txt File2.txt

join pairs up lines from two files where the join field is equal. By default, the join field is the first field, the fields are output in order except that the join field is not repeated, and non-pairable lines are skipped, which is exactly what you want.

Note that if the files have Windows line endings, they appear under Unix systems to have an extra carriage return character at the end of each line. The CR is mostly visually invisible, but as far as join and other text tools are concerned, it's a character like any one else, and it means the fields of File1.txt all end with a CR whereas the ones in File2.txt don't so they don't match. You need to strip the CR, at least in File1.txt.

<File1.txt tr -d '\r' | join -t : - File2.txt

You do need to sort the files. If they aren't, then ksh/bash/zsh, you can use process substitutions. (Add tr -d '\r' | if needed.)

join -t : <(sort File1.txt) <(sort File2.txt)

In plain sh, if your Unix variant has /dev/fd (most do), you can use that instead to pipe the output of two programs through two file descriptors.

sort File2.txt | { sort File1.txt | join -t : /dev/fd/0 /dev/fd/3; } 3<&1

If you need to preserve the original order of File1.txt and it isn't sorted by the join field, then add line numbers to remember the original order, sort by the join field, join, sort by line numbers and strip the line numbers. (You can do something similar if you want to preserver the order of the other file.)

<File1.txt nl -s : |
sort -t : -k 2 |
join -t : -1 2 - <(sort File2.txt) |
sort -t : -k 2,2n |
cut -d : -f 1,3
Related Question