Comparando dois arquivos no terminal linux


168

Existem dois arquivos chamados "a.txt" e "b.txt", ambos possuem uma lista de palavras. Agora, quero verificar quais palavras são extras em "a.txt" e não em "b.txt" .

Eu preciso de um algoritmo eficiente, pois preciso comparar dois dicionários.


27
diff a.txt b.txtnão é o suficiente?
ThanksForAllTheFish

As palavras podem ocorrer várias vezes em cada arquivo? Você pode classificar os arquivos?
Basile Starynkevitch

Eu preciso apenas daquelas palavras que não estão presentes em "b.txt" e estão presentes em a.txt
Ali Imran

Respostas:


343

Se você possui o vim instalado, tente o seguinte:

vimdiff file1 file2

ou

vim -d file1 file2

você achará fantástico.insira a descrição da imagem aqui


9
definitivamente incrível, bom design e fácil de descobrir as diferenças. Ohmygod
Zen

1
Sua resposta é impressionante, mas meu professor pediu que eu não usasse nenhuma função da biblioteca: P
Ali Imran

1
Que ótima ferramenta! Isso é imensamente útil.
user1205577

1
Quais são os significados dessas cores?
Zygimantus

1
Os códigos coloridos significam que são diferentes em dois arquivos. @zygimantus
Fengya Li

73

Classifique-os e use comm:

comm -23 <(sort a.txt) <(sort b.txt)

commcompara os arquivos de entrada (classificados) e, por padrão, gera três colunas: linhas exclusivas de a, linhas exclusivas de be linhas presentes em ambas. Especificando -1, -2e / ou -3você pode suprimir a saída correspondente. Portanto, comm -23 a blista apenas as entradas exclusivas de a. Eu uso a <(...)sintaxe para classificar os arquivos em tempo real, se eles já estiverem classificados, você não precisa disso.


Eu adicionei minha própria resposta usando apenas comandos grep, por favor me diga que é mais eficiente?
Ali Imran

3
@AliImran, commé mais eficiente porque faz o trabalho em uma única execução, sem armazenar o arquivo inteiro na memória. Como você está usando dicionários que provavelmente já estão classificados, você nem precisa sortdeles. O uso, grep -f file1 file2por outro lado, carregará o todo file1na memória e comparará cada linha file2com todas essas entradas, o que é muito menos eficiente. É principalmente útil para pequenas, sem classificação -f file1.
Anders Johansson

1
Obrigado @AndersJohansson por compartilhar o comando "comm". É bacana mesmo. Eu frequentemente tenho que fazer junções externas entre arquivos e isso faz o truque.
blispr

Preste atenção ao novo caractere de linha ... Acabei de descobrir que \ntambém será incluído para fazer comparações.
Bin

31

Experimente sdiff( man sdiff)

sdiff -s file1 file2

28

Você pode usar a diffferramenta no linux para comparar dois arquivos. Você pode usar as opções - formato de grupo alterado e - formato de grupo alterado - para filtrar os dados necessários.

As três opções a seguir podem ser usadas para selecionar o grupo relevante para cada opção:

  • '% <' obtém linhas de FILE1

  • '%>' obtém linhas de FILE2

  • '' (string vazia) para remover linhas de ambos os arquivos.

Por exemplo: diff --changed-group-format = "% <" --unchanged-group-format = "" file1.txt file2.txt

[root@vmoracle11 tmp]# cat file1.txt 
test one
test two
test three
test four
test eight
[root@vmoracle11 tmp]# cat file2.txt 
test one
test three
test nine
[root@vmoracle11 tmp]# diff --changed-group-format='%<' --unchanged-group-format='' file1.txt file2.txt 
test two
test four
test eight

27

Se você preferir o estilo de saída diff git diff, poderá usá-lo com o --no-indexsinalizador para comparar arquivos que não estão em um repositório git:

git diff --no-index a.txt b.txt

Usando um par de arquivos com cerca de 200k seqüências de nomes de arquivos em cada um deles, eu comparei (com o timecomando interno) essa abordagem contra algumas das outras respostas aqui:

git diff --no-index a.txt b.txt
# ~1.2s

comm -23 <(sort a.txt) <(sort b.txt)
# ~0.2s

diff a.txt b.txt
# ~2.6s

sdiff a.txt b.txt
# ~2.7s

vimdiff a.txt b.txt
# ~3.2s

commparece ser o mais rápido de longe, enquanto git diff --no-indexparece ser a abordagem mais rápida para saída no estilo diff.


Atualização 2018-03-25 Na verdade, você pode omitir o --no-indexsinalizador, a menos que esteja dentro de um repositório git e queira comparar arquivos não rastreados dentro desse repositório. Nas páginas do manual :

Este formulário é para comparar os dois caminhos fornecidos no sistema de arquivos. Você pode omitir a opção --no-index ao executar o comando em uma árvore de trabalho controlada pelo Git e pelo menos um dos pontos de caminhos fora da árvore de trabalho ou ao executar o comando fora de uma árvore de trabalho controlada pelo Git.




4

Use comm -13 (requer arquivos classificados) :

$ cat file1
one
two
three

$ cat file2
one
two
three
four

$ comm -13 <(sort file1) <(sort file2)
four

1

Aqui está a minha solução para isso:

mkdir temp
mkdir results
cp /usr/share/dict/american-english ~/temp/american-english-dictionary
cp /usr/share/dict/british-english ~/temp/british-english-dictionary
cat ~/temp/american-english-dictionary | wc -l > ~/results/count-american-english-dictionary
cat ~/temp/british-english-dictionary | wc -l > ~/results/count-british-english-dictionary
grep -Fxf ~/temp/american-english-dictionary ~/temp/british-english-dictionary > ~/results/common-english
grep -Fxvf ~/results/common-english ~/temp/american-english-dictionary > ~/results/unique-american-english
grep -Fxvf ~/results/common-english ~/temp/british-english-dictionary > ~/results/unique-british-english

2
Você tentou alguma das outras soluções? Uma dessas soluções foi útil para você? Sua pergunta é genérica o suficiente para atrair muitos usuários, mas sua resposta é mais específica para o meu gosto ... Para o meu caso em particular, sdiff -s file1 file2foi útil.
Metafaniel

@Metafaniel minha solução não usa o comando sdiff. Ele usa apenas comandos embutidos no linux para resolver o problema.
Ali Imran

-1

Usando awk para isso. Arquivos de teste:

$ cat a.txt
one
two
three
four
four
$ cat b.txt
three
two
one

O awk:

$ awk '
NR==FNR {                    # process b.txt  or the first file
    seen[$0]                 # hash words to hash seen
    next                     # next word in b.txt
}                            # process a.txt  or all files after the first
!($0 in seen)' b.txt a.txt   # if word is not hashed to seen, output it

Duplicatas são produzidas:

four
four

Para evitar duplicatas, adicione cada palavra recém-encontrada em a.txt ao seenhash:

$ awk '
NR==FNR {
    seen[$0]
    next
}
!($0 in seen) {              # if word is not hashed to seen
    seen[$0]                 # hash unseen a.txt words to seen to avoid duplicates 
    print                    # and output it
}' b.txt a.txt

Resultado:

four

Se as listas de palavras forem separadas por vírgula, como:

$ cat a.txt
four,four,three,three,two,one
five,six
$ cat b.txt
one,two,three

você tem que fazer algumas voltas extras ( forloops):

awk -F, '                    # comma-separated input
NR==FNR {
    for(i=1;i<=NF;i++)       # loop all comma-separated fields
        seen[$i]
    next
}
{
    for(i=1;i<=NF;i++)
        if(!($i in seen)) {
             seen[$i]        # this time we buffer output (below):
             buffer=buffer (buffer==""?"":",") $i
        }
    if(buffer!="") {         # output unempty buffers after each record in a.txt
        print buffer
        buffer=""
    }
}' b.txt a.txt

Saída desta vez:

four
five,six
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.