Diferença de dois arquivos grandes

14

Eu tenho "test1.csv" e ele contém

200,400,600,800
100,300,500,700
50,25,125,310

e test2.csv e ele contém

100,4,2,1,7
200,400,600,800
21,22,23,24,25
50,25,125,310
50,25,700,5

agora

diff test2.csv test1.csv > result.csv

é diferente de

diff test1.csv test2.csv > result.csv

Não sei qual é a ordem correta, mas quero outra coisa, os dois comandos acima produzirão algo como

2 > 100,4,2,1,7
   3 2,3c3,5
   4 < 100,300,500,700
   5 < 50,25,125,310
   6 \ No newline at end of file
   7 ---
   8 > 21,22,23,24,25
   9 > 50,25,125,310

Eu quero mostrar apenas a diferença, assim o results.csv deve ficar assim

100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5

Eu tentei diff -qe diff -seles não fizeram o truque. A ordem não importa, o que importa é que eu quero ver apenas a diferença, nem> nem <nem espaço em branco.

grep -FvF fez o truque em arquivos menores e não em arquivos grandes

o primeiro arquivo contém mais de 5 milhões de linhas, o segundo arquivo contém 1300.

portanto results.csv deve resultar em ~ 4.998.700 linhas

Eu também tentei o grep -F -x -v -f que não funcionou.

Lynob
fonte
1
@ Tim vi o seu link e eu sou um membro antigo, então eu sei as regras, mas foi descuidado, desculpe :) estava editando-o e vi um pop-up que o post foi editado para que você fizesse o trabalho para mim e eu sou Senhor agradecido.
Lynob
50,25,125,310é comum a ambos necessidade file..you para remover que a partir da saída desejada ..
heemayl
O pedido deve ser preservado?
kos
1
depende do que você quer fazer com a informação, diff, IMO, é para fazer um patch. De qualquer forma, agora tenho certeza da sua melhor ferramenta, diff, grep, awk ou perl.
Panther

Respostas:

20

Parece um trabalho para comm:

$ comm -3 <(sort test1.csv) <(sort test2.csv)
100,300,500,700
    100,4,2,1,7
    21,22,23,24,25
    50,25,700,5

Como explicado em man comm:

   -1     suppress column 1 (lines unique to FILE1)

   -2     suppress column 2 (lines unique to FILE2)

   -3     suppress column 3 (lines that appear in both files)

Portanto, -3significa que apenas as linhas exclusivas de um dos arquivos serão impressas. No entanto, esses são recuados de acordo com o arquivo em que foram encontrados. Para remover a guia, use:

$ comm -3 <(sort test1.csv) <(sort test2.csv) | tr -d '\t'
100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5

Nesse caso, você nem precisa classificar os arquivos e pode simplificar o acima para:

comm -3 test1.csv test2.csv | tr -d '\t' > difference.csv
Terdon
fonte
Você não foi enganado pelos espaços após a 200,[...]linha, hein? :)
kos
@ kos não, removi os espaços finais dos arquivos primeiro. Presumi que os arquivos do OP realmente não os tivessem.
terdon
6

Usando grepcom bashsubstituição de processo:

$ cat <(grep -vFf test2.csv test1.csv) <(grep -vFf test1.csv test2.csv)
100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5

Para salvar a saída como results.csv:

cat <(grep -vFf test2.csv test1.csv) <(grep -vFf test1.csv test2.csv) >results.csv
  • <()é o bashpadrão de substituição de processo

  • grep -vFf test2.csv test1.csv encontrará as linhas exclusivas para apenas test1.csv

  • grep -vFf test1.csv test2.csv encontrará as linhas exclusivas para apenas test2.csv

  • Finalmente, estamos resumindo os resultados por cat

Ou, como Oli sugeriu , você também pode usar o agrupamento de comandos:

$ { grep -vFf test2.csv test1.csv; grep -vFf test1.csv test2.csv; }
100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5

Ou simplesmente execute um após o outro, pois ambos estão escrevendo para STDOUT e serão adicionados:

$ grep -vFf test2.csv test1.csv; grep -vFf test1.csv test2.csv
100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5
heemail
fonte
1
Por que catdois comandos redirecionados? Por que não apenas executar um depois o outro? grep ... ; grep ...ou { grep ... ; grep ... ; }se você quisesse fazer algo com a saída coletiva.
Oli
@Oli Thanks..thats uma grande idea..i não pensei nisso ..
heemayl
4

Se a ordem das linhas não for relevante, use awkou perl:

awk '{seen[$0]++} END {for (i in seen) {if (seen[i] == 1) {print i}}}' 1.csv 2.csv

Use greppara obter as linhas comuns e filtrá-las:

grep -hxvFf <(grep -Fxf 1.csv 2.csv) 1.csv 2.csv

O grep interno obtém as linhas comuns e, em seguida, o grep externo encontra linhas que não correspondem a essas linhas comuns.

muru
fonte
Seu comando awk apenas reimplementa sort | uniq -u, o que fornece a resposta errada quando um arquivo contém linhas duplicadas. Para grep, eu diria "interno" / "externo", não "interno" / "externo".
Peter Cordes
@ PeterCordes sim, sim e quem é você para dizer que é o resultado errado?
Muru
Errado no sentido de que não é exatamente o que a pergunta pediu, naquele caso de canto. Pode ser que alguém quer, mas você deve apontar a diferença entre o que o seu awkvai imprimir e qual o comm -3e diffrespostas serão impressas.
Peter Cordes
@ PeterCordes de novo, quem é você para dizer isso? Até que o OP diga que é isso que eles querem, eu não me importo se a saída for diferente da de comm -3. Eu não vejo nenhuma razão para que eu deveria explicar isso. Se você deseja editar uma nota, fique à vontade.
Muru
O OP disse que quer a diferença. Isso nem sempre é o que seu programa produz. Um programa que produz a mesma saída para um caso de teste, mas não satisfaz a descrição escrita em todos os casos, requer atenção. Estou aqui para dizer isso, e é verdade, independentemente de quem eu sou ou quem você é. Eu adicionei uma nota.
Peter Cordes
4

Use as --*-line-format=...opções dediff

Você pode dizer diffexatamente o que precisa - explicado abaixo:

diff --old-line-format='%L' --new-line-format='%L' --unchanged-line-format='' f1.txt f2.txt

É possível especificar a saída do diff de uma maneira muito detalhada, semelhante a um printfformato numérico.

As linhas do primeiro arquivo test1.csvsão chamadas de linhas "antigas" e as do segundo test2.csvsão "novas". Isso faz sentido quando diffé usado para ver o que mudou em um arquivo.

As opções de que precisamos são as que definem o formato para as linhas "antigas", "novas" e "inalteradas".
Os formatos que precisamos são muito simples:
para as linhas alteradas, novas e antigas, queremos exibir apenas o texto das linhas. %Lé o símbolo de formato para o texto da linha.
Para as linhas inalteradas, não queremos mostrar nada.

Com isso, podemos escrever opções como --old-line-format='%L'e reunir tudo, usando seus dados de exemplo:

$ diff --old-line-format='%L' --new-line-format='%L' --unchanged-line-format='' test1.csv test2.csv
100,4,2,1,7
100,300,500,700
21,22,23,24,25
50,25,700,5


Notas sobre desempenho

Como os arquivos têm tamanho diferente, tente trocar os arquivos de entrada, se isso não interessar, pode ser que o funcionamento interno de diffpossa lidar de uma maneira melhor que a outra. Melhor é precisar de menos memória ou menos computação.

Há uma opção de otimização para uso diffcom arquivos grandes: --speed-large-files. Ele usa suposições sobre a estrutura do arquivo, portanto, não está claro se isso ajuda no seu caso, mas vale a pena tentar.

As opções de formato estão descritas man diffabaixo --LTYPE-line-format=LFMT.

Volker Siegel
fonte
3

Como o pedido não precisa ser preservado, basta:

sort test1.csv test2.csv | uniq -u
  • sort test1.csv test2.csv: mescla e classifica test1.csvetest2.csv
  • uniq -u: imprime apenas as linhas que não possuem duplicado
kos
fonte
Isso não funciona se um arquivo contém uma linha duas vezes, que não aparece no outro arquivo. Ambas as ocorrências teriam diffresultado.
Volker Siegel