É 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 campos 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 primeiro arquivo estiver sendo lido. -
c[$1$2]++; next
: se este for o primeiro arquivo, salve os primeiros dois campos na matrizc
. Em seguida, pule para a próxima linha para que isso seja aplicado apenas no primeiro arquivo. -
c[$1$2]>0
: o bloco else só será executado se este for o segundo arquivo, então verificamos se os campos 1 e 2 deste arquivo já foram vistos (c[$1$2]>0
) e se eles já foram, imprima a linha. Emawk
, a ação padrão é imprimir a linha, portanto, sec[$1$2]>0
for true, a linha será impressa.
Como alternativa, desde que você marcou com o Perl:
perl -e 'open(A, "file2"); while(<A>){/.+?\|[^|]+/ && $k{$&}++};
while(<>){/.+?\|[^|]+/ && do{print if defined($k{$&})}}' file1
Explicação
A primeira linha abrirá file2
, lerá tudo até o 2º |
( .+?\|[^|]+
) e salvará isso (o $&
é 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 imprime a linha se essas colunas estiverem definidas no %k
hash.
Ambas as abordagens acima precisarão manter 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ê pode fazer algo como
cut -d'|' -f 1,2 file2 | while read pat; do grep "^$pat" file1; done
Mas isso será mais lento.