Encontre os arquivos existentes em um diretório, mas não no outro [fechado]


295

Estou tentando encontrar os arquivos existentes em um diretório, mas não no outro, tentei usar este comando:

diff -q dir1 dir2

O problema com o comando acima em que ele encontra os arquivos, dir1mas não está dir2, assim como os arquivos, dir2mas não está dir1,

Estou tentando encontrar os arquivos, dir1mas não dir2apenas.

Aqui está uma pequena amostra da aparência dos meus dados

dir1    dir2    dir3
1.txt   1.txt   1.txt
2.txt   3.txt   3.txt
5.txt   4.txt   5.txt
6.txt   7.txt   8.txt

Outra pergunta em minha mente é como encontrar os arquivos, dir1mas não em dir2ou dir3em um único comando?

Respostas:


390
diff -r dir1 dir2 | grep dir1 | awk '{print $4}' > difference1.txt

Explicação:

  • diff -r dir1 dir2 mostra quais arquivos estão apenas no diretório1 e aqueles somente no diretório2 e também as alterações dos arquivos presentes nos dois diretórios, se houver.

  • diff -r dir1 dir2 | grep dir1 mostra quais arquivos estão apenas no dir1

  • awk para imprimir apenas o nome do arquivo.


5
Eu gostaria grepde ^dir1ter certeza de não dir1aparecer mais tarde no caminho.
Alfe 28/05

@ Alfe Pode ser melhorado. Eu uso $4como exemplo. De fato, no meu Ubuntu atual, diffresponde em italiano. $4é ok para as respostas italiano e inglês, mas eu não tenho certeza que para cada outras línguas ...
asclepix

139

Isso deve fazer o trabalho:

diff -rq dir1 dir2

Opções explicadas (através da página do manual diff (1) ):

  • -r - Compare recursivamente todos os subdiretórios encontrados.
  • -q - Saída apenas se os arquivos diferirem.

8
Agradável! Mas eu acho que deve ser alargado assim:diff -rq dir1 dir2 | grep 'Only in dir1/'
sobi3ch

2
É uma comparação por conteúdo, mas pode demorar muito em unidades lentas.
precisa saber é o seguinte

5
Apenas uma observação sobre a -qopção: As páginas de manual dizem apenas "Saída apenas se os arquivos diferem", não como ele verifica se são diferentes. Examinei o código-fonte e descobri que ele apenas verifica os tamanhos dos arquivos para determinar diferenças, não o conteúdo real.
Ryancdotnet

No que diz respeito à -qopção, não posso reproduzir que apenas verifique o tamanho do arquivo. Usando o GNU Diffutils 3.7, comparando dois arquivos com o mesmo tamanho, mas com conteúdo diferente com diff -q file1 file2saídas Files file1 and file2 differ.
Stefan Schmidt

50
comm -23 <(ls dir1 |sort) <(ls dir2|sort)

Este comando fornecerá os arquivos que estão no diretório1 e não no diretório2.

Sobre o <( )sinal, você pode pesquisar no Google como 'substituição de processo'.


que seria bom para o trabalho também com subdiretórios, eu acho que (ls -R dir1|sort)poderia fazer o truque
ulkas

1
Isso funcionaria no modo de recuperação do OS X.
Anthony Vanover 19/09/16

@ulkas, a saída pode estar incorreta se você usar (ls -R dir|sort).
precisa saber é o seguinte

3
O vimdiff fornece uma comparação visual muito mais agradável com o realce de cores:vimdiff <(ls dir1 |sort) <(ls dir2|sort)
Logan Reed

32

Uma boa maneira de fazer essa comparação é usar findcom md5sum, então a diff.

Exemplo:

Use findpara listar todos os arquivos no diretório, calcule o hash md5 para cada arquivo e envie-o para um arquivo:

find /dir1/ -type f -exec md5sum {} \; > dir1.txt

Faça o mesmo procedimento para o outro diretório:

find /dir2/ -type f -exec md5sum {} \; > dir2.txt

Em seguida, compare o resultado dois arquivos com "diff":

diff dir1.txt dir2.txt

Essa estratégia é muito útil quando os dois diretórios a serem comparados não estão na mesma máquina e você precisa garantir que os arquivos sejam iguais nos dois diretórios.

Outra boa maneira de fazer o trabalho é usar git

git diff --no-index dir1/ dir2/

Cumprimentos!


1
Eu não git poderia fazer uma diferença em diretórios arbitrários que não estão dentro de um repositório git ... incrível !!! Esta resposta apenas resolveu um grande problema para mim, obrigado
ViktorNova

17

O Meld ( http://meldmerge.org/ ) faz um ótimo trabalho na comparação de diretórios e arquivos.

Meld comparando diretórios


Exceto que o meld faz um péssimo trabalho quando se trata de finais de linha ...
0xC0000022L 24/02

1
Nunca tive um problema com terminações de linha. Você pode detalhar?
Catalin Hritcu

Sim, não indica as terminações da linha. Isso (repetidamente) levou os desenvolvedores a usarem essa ferramenta para confirmar alterações que "consertavam" as terminações de linha, transformando um CRLF em CRLFLF, por exemplo.
0xC0000022L

3
Ele também insiste na leitura do conteúdo do arquivo e, portanto, é quase inútil nos diretórios >> 1GB.
Tomislav Nakic-Alfirevic

13

O plugin DirDiff do vim é outra ferramenta muito útil para comparar diretórios.

vim -c "DirDiff dir1 dir2"

Ele não apenas lista quais arquivos são diferentes entre os diretórios, mas também permite que você inspecione / modifique com vimdiff os arquivos que são diferentes.


11

Insatisfeito com todas as respostas, como a maioria delas trabalha muito lentamente e produz uma saída desnecessariamente longa para diretórios grandes, escrevi meu próprio script Python para comparar duas pastas.

Ao contrário de muitas outras soluções, ele não compara o conteúdo dos arquivos. Além disso, ele não entra em subdiretórios ausentes em outro diretório. Portanto, a saída é bastante concisa e o script funciona rápido.

#!/usr/bin/env python3

import os, sys

def compare_dirs(d1: "old directory name", d2: "new directory name"):
    def print_local(a, msg):
        print('DIR ' if a[2] else 'FILE', a[1], msg)
    # ensure validity
    for d in [d1,d2]:
        if not os.path.isdir(d):
            raise ValueError("not a directory: " + d)
    # get relative path
    l1 = [(x,os.path.join(d1,x)) for x in os.listdir(d1)]
    l2 = [(x,os.path.join(d2,x)) for x in os.listdir(d2)]
    # determine type: directory or file?
    l1 = sorted([(x,y,os.path.isdir(y)) for x,y in l1])
    l2 = sorted([(x,y,os.path.isdir(y)) for x,y in l2])
    i1 = i2 = 0
    common_dirs = []
    while i1<len(l1) and i2<len(l2):
        if l1[i1][0] == l2[i2][0]:      # same name
            if l1[i1][2] == l2[i2][2]:  # same type
                if l1[i1][2]:           # remember this folder for recursion
                    common_dirs.append((l1[i1][1], l2[i2][1]))
            else:
                print_local(l1[i1],'type changed')
            i1 += 1
            i2 += 1
        elif l1[i1][0]<l2[i2][0]:
            print_local(l1[i1],'removed')
            i1 += 1
        elif l1[i1][0]>l2[i2][0]:
            print_local(l2[i2],'added')
            i2 += 1
    while i1<len(l1):
        print_local(l1[i1],'removed')
        i1 += 1
    while i2<len(l2):
        print_local(l2[i2],'added')
        i2 += 1
    # compare subfolders recursively
    for sd1,sd2 in common_dirs:
        compare_dirs(sd1, sd2)

if __name__=="__main__":
    compare_dirs(sys.argv[1], sys.argv[2])

Uso da amostra:

user@laptop:~$ python3 compare_dirs.py dir1/ dir2/
DIR  dir1/out/flavor-domino removed
DIR  dir2/out/flavor-maxim2 added
DIR  dir1/target/vendor/flavor-domino removed
DIR  dir2/target/vendor/flavor-maxim2 added
FILE dir1/tmp/.kconfig-flavor_domino removed
FILE dir2/tmp/.kconfig-flavor_maxim2 added
DIR  dir2/tools/tools/LiveSuit_For_Linux64 added

Ou se você deseja ver apenas os arquivos do primeiro diretório:

user@laptop:~$ python3 compare_dirs.py dir2/ dir1/ | grep dir1
DIR  dir1/out/flavor-domino added
DIR  dir1/target/vendor/flavor-domino added
FILE dir1/tmp/.kconfig-flavor_domino added

PS Se você precisar comparar tamanhos de arquivo e hashes de arquivo para possíveis alterações, publiquei um script atualizado aqui: https://gist.github.com/amakukha/f489cbde2afd32817f8e866cf4abe779


Script bastante simples que faz exatamente o que eu queria: Verifique uma cópia em massa: +1 de mim. (necessário converter para python2) Dica: o uso de conjuntos pode tornar a parte diff mais simples.
21818 Jason Morgan

6

Outra abordagem (talvez mais rápida para diretórios grandes):

$ find dir1 | sed 's,^[^/]*/,,' | sort > dir1.txt && find dir2 | sed 's,^[^/]*/,,' | sort > dir2.txt
$ diff dir1.txt dir2.txt

O sedcomando remove o primeiro componente do diretório, graças à postagem de Erik )


1
Eu acredito que esse método é mais simples (ainda usando, findportanto, um comentário e não uma resposta separada): cd dir2; find . -exec [ -e ../dir1/{} ] \; -o -print 2>/dev/null Isso imprimirá arquivos presentes no dir2, mas não presentes no dir1.
Alexander Amelkin 28/09

5

Isso é um pouco tarde, mas pode ajudar alguém. Não tenho certeza se diff ou rsync cuspir apenas nomes de arquivos em um formato simples como este. Agradeço ao plhn por fornecer a solução legal que expus abaixo.

Se você quiser apenas os nomes dos arquivos, para facilitar a cópia dos arquivos necessários em um formato limpo, use o comando find.

comm -23 <(find dir1 | sed 's/dir1/\//'| sort) <(find dir2 | sed 's/dir2/\//'| sort) | sed 's/^\//dir1/'

Isso pressupõe que dir1 e dir2 estão na mesma pasta pai. O sed apenas remove a pasta pai para que você possa comparar maçãs com maçãs. O último sed apenas coloca o nome dir1 de volta.

Se você apenas deseja arquivos:

comm -23 <(find dir1 -type f | sed 's/dir1/\//'| sort) <(find dir2 -type f | sed 's/dir2/\//'| sort) | sed 's/^\//dir1/'

Da mesma forma para diretórios:

comm -23 <(find dir1 -type d | sed 's/dir1/\//'| sort) <(find dir2 -type d | sed 's/dir2/\//'| sort) | sed 's/^\//dir1/'

1
Note que você poderia fazer cdantes da findvez de ter que usar sed, por exemplo: comm -23 <(cd dir1 || exit; find -type f | sort) <(cd dir2 || exit; find -type f | sort). (As exits estão aqui para evitar findde usar o diretório atual deve cdfalhar.)
PHK

Observe também que sua solução pode falhar quando arquivos com determinados caracteres especiais estiverem presentes; se você tiver uma versão muito recente do commwith support -z(fornecida com git.savannah.gnu.org/cgit/coreutils.git/commit/… ), poderá fazê-lo comm -23 -z <(cd dir1 && find -type f -print0 | sort -z) <(cd dir2 && find -type f -print0 | sort -z). (Nesse meio tempo eu também descobri que as exits poderiam ser substituídos.)
PHK

5

A resposta aceita também listará os arquivos existentes nos dois diretórios, mas com conteúdo diferente. Para listar apenas os arquivos que existem no dir1, você pode usar:

diff -r dir1 dir2 | grep 'Only in' | grep dir1 | awk '{print $4}' > difference1.txt

Explicação:

  • diff -r dir1 dir2: compare
  • grep 'Only in': obtém linhas que contêm 'Only in'
  • grep dir1: obtém linhas que contêm dir

5

Esta resposta otimiza uma das sugestões do @ Adail-Junior adicionando a -Dopção, o que é útil quando nenhum dos diretórios comparados é repositório git:

git diff -D --no-index dir1/ dir2/

Se você usar -D, não verá comparações com /dev/null: text Binary files a/whatever and /dev/null differ


Foi muito útil na comparação de dois diretórios, você vê instantaneamente as diferenças entre os arquivos. Obviamente, está funcionando melhor em arquivos com conteúdo de texto.
Erich Kuester

1

Uma maneira simplificada de comparar 2 diretórios usando o comando DIFF

diff filename.1 filename.2> filename.dat >> Enter

abra filename.dat após a conclusão da execução

e você verá: Only in filename.1: filename.2 Only in: directory_name: name_of_file1 Only in: directory_Name: name_of_file2


Por que você precisa gerar um arquivo .dat?
Vishnu NK

1

Este é o script bash para imprimir comandos para sincronizar dois diretórios

dir1=/tmp/path_to_dir1
dir2=/tmp/path_to_dir2
diff -rq $dir1 $dir2 | sed -e "s|Only in $dir2\(.*\): \(.*\)|cp -r $dir2\1/\2 $dir1\1|" |  sed -e "s|Only in $dir1\(.*\): \(.*\)|cp -r $dir1\1/\2 $dir2\1|" 

0

GNU greppode inverter a pesquisa com a opção -v. Isso torna o greprelatório das linhas, que não coincidem. Com isso, você pode remover os arquivos dir2da lista de arquivos dir1.

grep -v -F -x -f <(find dir2 -type f -printf '%P\n') <(find dir1 -type f -printf '%P\n')

As opções -F -xindicam greppara executar uma pesquisa de string em toda a linha.

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.