compara duas colunas de arquivos diferentes e imprime se corresponder a

16

Estou usando o Solaris 10 e as opções do grep envolvendo -f não funcionam.

Eu tenho dois arquivos separados por pipe:

arquivo1:

abc|123|BNY|apple|
cab|234|cyx|orange|
def|kumar|pki|bird|

arquivo 2:

abc|123|
kumar|pki|
cab|234

Gostaria de comparar as duas primeiras colunas do arquivo2 com o arquivo1 (pesquisar todo o conteúdo do arquivo1 nas duas primeiras colunas) se elas corresponderem à impressão da linha correspondente do arquivo1. Em seguida, procure a segunda linha do arquivo 2 e assim por diante.

Resultado esperado:

abc|123|BNY|apple|
cab|234|cyx|orange|

Os arquivos que eu tenho são enormes, contendo cerca de 400.000 linhas, então eu gostaria de fazer a execução rápida.

    
por user68365 06.06.2014 / 12:19

4 respostas

21

É 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 matriz c . 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. Em awk , a ação padrão é imprimir a linha, portanto, se c[$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.

    
por 06.06.2014 / 12:38
1

Eu acho que

grep -Ff file2 file1

é o que você está procurando. Deve ser eficiente, mas não tenho certeza se será tão preciso quanto você quiser. Se abc|123 (por exemplo) for encontrado em uma linha em file1 em colunas diferentes, essa linha também será impressa. Se você puder garantir que isso nunca acontecerá, a linha acima deve funcionar.

    
por 06.06.2014 / 12:24
1

Se você gostaria de pensar no problema do SQL da mesma forma, então você deve tentar uma ferramenta chamada ' q ':

$ q -d '|' "select f1.* from file1 f1 join file2 f2 on (f1.c1 = f2.c1 and f1.c2 = f2.c2)"

É mais claro e fácil de entender se você estiver familiarizado com a consulta SQL.

    
por 09.12.2015 / 15:05
0
$  sed 's/^/\^/' 2.txt > temp.txt ; grep 1.txt -f temp.txt
abc|123|BNY|apple|
cab|234|cyx|orange|
    
por 06.06.2014 / 12:31