como mostrar linhas em comum (diff reverso)?

170

Eu tenho uma série de arquivos de texto para os quais gostaria de conhecer as linhas em comum e não as linhas diferentes entre elas. Linha de comando unix ou windows está bem.

foo:

linux-vdso.so.1 =>  (0x00007fffccffe000)
libvlc.so.2 => /usr/lib/libvlc.so.2 (0x00007f0dc4b0b000)
libvlccore.so.0 => /usr/lib/libvlccore.so.0 (0x00007f0dc483f000)
libc.so.6 => /lib/libc.so.6 (0x00007f0dc44cd000)

Barra:

libkdeui.so.5 => /usr/lib/libkdeui.so.5 (0x00007f716ae22000)
libkio.so.5 => /usr/lib/libkio.so.5 (0x00007f716a96d000)
linux-vdso.so.1 =>  (0x00007fffccffe000)

Portanto, considerando esses dois arquivos acima, a saída do utilitário desejado seria semelhante a file1:line_number, file2:line_number == matching text (apenas uma sugestão, eu realmente não me importo com a sintaxe):

foo:1, bar:3 == linux-vdso.so.1 =>  (0x00007fffccffe000)

obrigado.

Matt Wilson
fonte
@ChristopherSchultz My error. A 1ª linha do 1º exemplo deve corresponder à última linha do 2º exemplo. Obrigado por pegar o erro; mudando.
Matt Wilkie
1
Outra pergunta semelhante com boas respostas: unix.stackexchange.com/questions/1079/…
MortezaE

Respostas:

210

No * nix, você pode usar comm . A resposta para a pergunta é:

comm -1 -2 file1.sorted file2.sorted 
# where file1 and file2 are sorted and piped into *.sorted

Aqui está o uso completo de comm:

comm [-1] [-2] [-3 ] file1 file2
-1 Suppress the output column of lines unique to file1.
-2 Suppress the output column of lines unique to file2.
-3 Suppress the output column of lines duplicated in file1 and file2. 

Observe também que é importante classificar os arquivos antes de usar o comm, conforme mencionado nas páginas de manual.

Dan Lew
fonte
3
comm [-1] [-2] [-3] arquivo1 arquivo2 -1 Suprima a coluna de saída de linhas exclusivas do arquivo1. -2 Suprima a coluna de saída de linhas exclusivas do arquivo2. -3 Suprima a coluna de saída de linhas duplicadas no arquivo1 e no arquivo2.
ojblass
@ojblass: Adicionado isso à resposta.
Matt J
6
Eu descobri que é importante que os arquivos sejam classificados antes de usar o comm. Talvez adicione isso à resposta.
mate Wilkie
11
curta resposta à pergunta: comm -1 -2 arq1 arq2
greggles
6
Você pode usar isso se seus arquivos não estiverem classificados: comm -1 -2 <(classificar nome do arquivo1) <(classificar nome do arquivo2)
Kevin Wheeler
56

Encontrei esta resposta em uma pergunta listada como duplicada . Acho que o grep é mais amigável ao administrador que o comm, portanto, se você quiser apenas o conjunto de linhas correspondentes (útil para comparar CSVs, por exemplo), basta usar

grep -F -x -f file1 file2

ou a versão simplificada do fgrep

fgrep -xf file1 file2

Além disso, você pode usar o file2*glob e procurar linhas em comum com vários arquivos, em vez de apenas dois.

Algumas outras variações úteis incluem

  • -n flag para mostrar o número da linha de cada linha correspondente
  • -c para contar apenas o número de linhas que correspondem
  • -vpara exibir apenas as linhas no arquivo2 que diferem (ou usam diff).

O uso commé mais rápido, mas essa velocidade custa às custas de ter que classificar seus arquivos primeiro. Não é muito útil como um 'diff reverso'.

Ryder
fonte
graças à Ryder, isso poderia ser mais útil do que o de muitos. Você deve vincular à resposta da fonte (há mais de meia dúzia vinculada no Q na navegação à direita; é um pouco de trabalho a encontrar). Também seria bom saber como o grep se sai bem com entradas classificadas de maneira diferente ou diferente e pode imprimir os respectivos números de linha de correspondências.
Matt Wilkie
1
@mattwilkie Senti a necessidade de voltar e esclarecer o uso da -vbandeira depois que eu mesma a usei . Digamos que você tenha dois arquivos csv file1 e file2 e eles tenham linhas sobrepostas e não sobrepostas. Se você quiser todas e apenas as linhas não sobrepostas, use fgrep -v file1 file2retornará apenas as linhas não sobrepostas no arquivo2 e nenhuma das linhas não sobrepostas adicionais no arquivo1 . Isso pode ser óbvio para alguns, mas é melhor afirmar o óbvio do que a má interpretação do risco. Nesse caso específico, classificar e usar os arquivos commainda é a melhor escolha.
Ryder
1
Obrigado por voltar e esclarecer a Ryder. A atenção extra é notada e apreciada (tudo é fácil de deixar escapar coisas antigas!). Mudei a resposta aceita porque comm é claramente a escolha da comunidade, embora pessoalmente eu ainda a use quando a classificação é uma sobrecarga indesejada.
22615 Mattel
2
Outra complicação ao usar grep: qualquer linha em branco no primeiro arquivo corresponderá a todas as linhas no segundo arquivo. Verifique se file1não há linhas em branco ou os arquivos serão idênticos.
Christopher Schultz
grep -Fxfé para mim.
Loxaxs 17/03/19
35

Foi perguntado aqui antes: comando Unix para encontrar linhas comuns em dois arquivos

Você também pode tentar com perl (o crédito vai aqui )

perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/'  file1 file2
ChristopheD
fonte
1
obrigado. Eu gostaria de aceitar as duas respostas, pois o liner perl one é multiplataforma. Comm recebe o aceno porque é mais simples.
mate Wilkie
1
Perfeito. Usando o cygwin terminal no windows e commnão estava prontamente disponível. Essa foi a alternativa perfeita.
Qix - MONICA FOI ERRADA
3
Isso não se importa com a forma como as linhas são ordenadas. É mais preciso que o comm.
enl8enmentnow
1
Uma explicação está aqui: stackoverflow.com/questions/17552789/…
Chris Koknat
17

Acabei de aprender o comando comm neste segmento, mas queria adicionar algo extra: se os arquivos não forem classificados e você não quiser tocar nos arquivos originais, poderá canalizar o outptut do comando sort. Isso deixa os arquivos originais intactos. Funciona no bash, não posso dizer sobre outras conchas.

comm -1 -2 <(sort file1) <(sort file2)

Isso pode ser estendido para comparar a saída do comando, em vez dos arquivos:

comm -1 -2 <(ls /dir1 | sort) <(ls /dir2 | sort)
Greg Mueller
fonte
9

A maneira mais fácil de fazer é:

awk 'NR==FNR{a[$1]++;next} a[$1] ' file1 file2

Os arquivos não são necessários para serem classificados.

Gopu
fonte
1
Isso é diferente da maioria das respostas aqui, pois permite reconstruir modelos de origem. Eu tenho dois arquivos criados a partir do mesmo invólucro, com texto diferente inserido em alguns pontos. Esta resposta me permitiu recuperar o wrapper.
Lucas Gonze 3/17/17
1

Apenas para informação, criei uma pequena ferramenta para o Windows fazendo a mesma coisa que "grep -F -x -f arquivo1 arquivo2" (como não encontrei nada equivalente a este comando no Windows)

Aqui está: http://www.nerdzcore.com/?page=commonlines

O uso é "CommonLines inputFile1 inputFile2 outputFile"

O código fonte também está disponível (GPL)

Zivilyn Bane
fonte
1

No Windows, você pode usar um script do Powershell com CompareObject

compare-object -IncludeEqual -ExcludeDifferent -PassThru (get-content A.txt) (get-content B.txt)> MATCHING.txt | Out-Null #Find Matching Lines

CompareObject:

  • IncludeEqual sem -ExcludeDifferent: Tudo
  • ExcludeDifferent sem -InclueEqual: Nothing
Picanço
fonte