É para isso que o awk foi projetado:
$ awk -F'|' 'NR==FNR{c[$1$2]++;next};c[$1$2] > 0' file2 file1
abc|123|BNY|apple|
cab|234|cyx|orange|
Explicação
-F'|'
: define o separador de campo para |
.
NR==FNR
: NR é o número da linha de entrada atual e FNR o número da linha do arquivo atual. Os dois serão iguais apenas enquanto o 1º arquivo estiver sendo lido.
c[$1$2]++; next
: se esse for o 1º arquivo, salve os dois primeiros campos na c
matriz. Em seguida, pule para a próxima linha para que isso seja aplicado apenas no 1º arquivo.
c[$1$2]>0
: o bloco else será executado somente se este for o segundo arquivo, portanto verificamos se os campos 1 e 2 deste arquivo já foram vistos ( c[$1$2]>0
) e, se foram, imprimimos a linha. Em awk
, a ação padrão é imprimir a linha e, se c[$1$2]>0
for verdadeira, a linha será impressa.
Como alternativa, desde que você marcou com Perl:
perl -e 'open(A, "file2"); while(<A>){/.+?\|[^|]+/ && $k{$&}++};
while(<>){/.+?\|[^|]+/ && do{print if defined($k{$&})}}' file1
Explicação
A primeira linha será aberta file2
, leia tudo até a segunda |
( .+?\|[^|]+
) e salve ( $&
o resultado do último operador de correspondência) no %k
hash.
A segunda linha processa o arquivo1, usa o mesmo regex para extrair as primeiras duas colunas e imprimir a linha se essas colunas estiverem definidas no %k
hash.
Ambas as abordagens acima precisarão conter as 2 primeiras colunas do arquivo2 na memória. Isso não deve ser um problema se você tiver apenas algumas centenas de milhares de linhas, mas se for, você poderia fazer algo como
cut -d'|' -f 1,2 file2 | while read pat; do grep "^$pat" file1; done
Mas isso será mais lento.