Existe uma ferramenta para obter as linhas em um arquivo que não estão em outro?

Respostas:

159

Sim. A grepferramenta padrão para procurar arquivos por cadeias de texto pode ser usada para subtrair todas as linhas de um arquivo de outro.

grep -F -x -v -f fileB fileA

Isso funciona usando cada linha no arquivo B como um padrão ( -f fileB) e tratando-a como uma sequência simples para corresponder (não uma expressão regular regular) ( -F). Você força a correspondência a ocorrer em toda a linha ( -x) e imprime apenas as linhas que não correspondem ( -v). Portanto, você está imprimindo as linhas no arquivo A que não contêm os mesmos dados que qualquer linha no arquivoB.

A desvantagem desta solução é que ela não leva em consideração a ordem das linhas e, se sua entrada tiver linhas duplicadas em locais diferentes, você poderá não obter o que espera. A solução para isso é usar uma ferramenta de comparação real, como diff. Você pode fazer isso criando um arquivo diff com o valor de contexto em 100% das linhas no arquivo e analisando-o apenas para as linhas que seriam removidas se converter o arquivo A para o arquivo B. (Observe que este comando também remove o diff depois de obter as linhas corretas.)

diff -U $(wc -l < fileA) fileA fileB | sed -n 's/^-//p' > fileC
Caleb
fonte
@ inderpreet99 Na -uverdade, o argumento em minúsculas aceita um parâmetro de um número, desde que não seja seguido por um espaço. A vantagem da maneira que eu tinha antes é que ele funcionará com ou sem um valor, para que você possa usar algo nessa rotina de subcomando que não retornou saída. A letra maiúscula '-U', por outro lado, requer um argumento.
Caleb
tenha cuidado, grep -f é O (N ^ 2) Eu acredito: stackoverflow.com/questions/4780203/…
rogerdpack 16/15
11
o diffpipeline funciona muito bem, obrigado.
Felipe Alvarez
Para explicar o problema de classificação, você pode usar a substituição de processo no comando para processar cada arquivo antes do grepconforme necessário. Exemplo:grep -F -x -v -f <(sort fileB) <(sort fileA)
Tony Cesaro
@TonyCesaro Isso funcionaria se o seu conjunto de dados não fosse específico do pedido e as duplicatas não precisassem ser levadas em consideração. A vantagem de usar diffé que a posição no arquivo é levada em consideração.
Caleb #
57

A resposta depende muito do tipo e formato dos arquivos que você está comparando.

Se os arquivos que você está comparando são arquivos de texto classificados, a ferramenta GNU escrita por Richard Stallman e Davide McKenzie chamada commpode executar a filtragem que você procura. Faz parte dos coreutils.

Exemplo

Digamos que você tenha os 2 arquivos a seguir:

$ cat a
1
2
3
4
5

$ cat b
1
2
3
4
5
6

Linhas no arquivo bque não estão no arquivo a:

$ comm <(sort a) <(sort b) -3
    6
Um amigo
fonte
11
+1 para mencionar comm; infelizmente, commrequer arquivos classificados
Arcege
11
então classificá-los? comm <(classificar a) <(classificar b) -1 -2
Sirex
Essa é uma sintaxe estranha. <()? Funciona e eu entendo, mas existe um nome para essa estranheza?
mlissner
2
O @mlissner <()também é conhecido como substituição de processo .
Miku #
11
commfoi originalmente escrito por volta de 1973 por alguém do Bell Labs, não por rms. Você está se referindo à implementação do GNU, que veio muito depois. Houve muitas implementações diferentes dos utilitários Unix ao longo dos anos.
Stéphane Chazelas
32

de stackoverflow ...

comm -23 arquivo1 arquivo2

-23 suprime as linhas que estão nos dois arquivos ou apenas no arquivo 2. Os arquivos precisam ser classificados (eles estão no seu exemplo), mas, se não estiverem, direcione-os primeiro pela classificação ...

Veja a página de manual aqui

JJS
fonte
Isso não funciona para mim, por alguma razão ...
Jan
@ Jan seus arquivos estão classificados? Como você os classificou?
JJS
8

Os métodos grep e comm (com classificação) demoram muito tempo em arquivos grandes. O SiegeX e o ghostdog74 compartilharam dois ótimos métodos awk para extrair linhas exclusivas para um dos dois arquivos no Stack Overflow:

$ awk 'FNR==NR{a[$0]++}FNR!=NR && !a[$0]{print}' file1 file2

$ awk 'FNR==NR{a[$0]++;next}(!($0 in a))' file1 file2
Miles Wolbe
fonte
2
Se você estiver fazendo isso com arquivos enormes, as restrições de memória ao carregar um arquivo enorme em uma matriz associativa serão proibitivas.
Charles Duffy
4

Se os arquivos forem grandes e você não tiver um pedido personalizado para suas entradas, o grep levará muito tempo. Uma alternativa rápida seria

sort file1 > 1 
sort file2 > 2 
diff 1 2 | grep "\>" | sed -e 's/> //'

[arquivo2-arquivo1 resulta em tela, canal para arquivo etc.]

Mudar >para <obteria a subtração oposta.rm 1 2

Eshel Faraggi
fonte
2

Você também pode considerar o vimdiff, que destaca as diferenças entre os arquivos em um editor do vim

simona
fonte
11
Mas existe uma maneira fácil de fazer automaticamente a subtração no Vimdiff?
Kazark