compara dois arquivos e imprime correspondências - arquivos grandes

4

Eu preciso comparar dois arquivos e imprimir linhas correspondentes. Se file1 username estiver no arquivo2 (campo 1), quero imprimi-lo no novo arquivo correspondente.

Arquivo1.txt:

Hey123
Johnson
Hanny123
Fanny

(o arquivo1 é de 240MB - 20.000.000 linhas)

Arquivo2.txt:

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

(file2 é de 1,4 GB - 69.000.000 linhas)

Saída de linhas correspondentes esperadas:

Hanny123:tronda9
Hey123:groll

Eu tenho tentado por 4 horas sem sucesso. Ambos os arquivos são classificados e eu tentei juntar + inúmeros comandos grep / awk. Meu grande problema é o esgotamento de RAM. Adoraria alguma ajuda como eu poderia abordar isso, arquivos tão grandes.

    
por Axel Tobieson 06.03.2017 / 00:10

3 respostas

5

Se os arquivos estão classificados (as amostras que você postou são) então é tão simples quanto

join -t : File1.txt File2.txt

join pares de linhas de dois arquivos em que o campo de junção é igual. Por padrão, o campo de junção é o primeiro campo, os campos são exibidos em ordem, exceto que o campo de junção não é repetido e as linhas não pareadas são ignoradas, o que é exatamente o que você deseja.

Observe que, se os arquivos tiverem finais de linha do Windows , eles aparecerão em sistemas Unix para obter um retorno de carro extra personagem no final de cada linha. O CR é visualmente invisível, mas no que diz respeito a join e outras ferramentas de texto, é um personagem como qualquer outro, e isso significa que os campos de File1.txt terminam com um CR, enquanto os de File2.txt não para que eles não combinem. Você precisa remover o CR, pelo menos em File1.txt .

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

Você precisa classificar os arquivos. Se não estiverem, então ksh / bash / zsh, você pode usar substituições de processos. (Adicione tr -d '\r' | , se necessário.)

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

Em sh simples, se sua variante Unix tiver /dev/fd (a maioria faz), você pode usá-la para canalizar a saída de dois programas por meio de dois descritores de arquivo.

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

Se você precisar preservar a ordem original de File1.txt e ela não estiver classificada pelo campo de associação, adicione números de linha para lembrar o pedido original, classifique pelo campo de associação, junte-se, classifique por números de linha e retire os números de linha. (Você pode fazer algo semelhante se quiser preservar a ordem do outro arquivo.)

<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
    
por 06.03.2017 / 01:09
3

Se você tem 2Gbyte de RAM livre, tente

awk -F: 'NR==FNR { n[$0]++ ; next}; $1 in n ' file1 file2 > file3
    
por 06.03.2017 / 00:56
0

Uma possível solução (que certamente funciona para o seu pequeno exemplo):

#!/bin/bash

# because File2.txt is bigger, it gets the main loop.
# read each line of File2.txt
while read string; do
    # read each line of File1.txt
    while read string2; do
    # check match, and write if needed.
    if [[ $string == *"$string2"* ]]; then
        echo $string >> match_output.txt
        echo "wrote "$string" to match_output.txt..."
    fi
    done < File1.txt
done < File2.txt
    
por 06.03.2017 / 04:46