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.



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

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

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.


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.


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

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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.