Comparando o conteúdo de dois diretórios

85

Eu tenho dois diretórios que devem conter os mesmos arquivos e ter a mesma estrutura de diretórios.

Acho que algo está faltando em um desses diretórios.

Usando o bash shell, existe uma maneira de comparar meus diretórios e ver se um deles está faltando arquivos que estão presentes no outro?

    
por AndreaNobili 16.02.2014 / 17:54

9 respostas

57

Uma boa maneira de fazer essa comparação é usar find com md5sum e, em seguida, diff .

Exemplo

Use find para listar todos os arquivos no diretório, em seguida, calcule o hash md5 para cada arquivo e canalize-o por nome de arquivo para um arquivo:

find /dir1/ -type f -exec md5sum {} + | sort -k 2 > dir1.txt

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

find /dir2/ -type f -exec md5sum {} + | sort -k 2 > dir2.txt

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

diff -u dir1.txt dir2.txt

Ou como um único comando usando a substituição de processos:

diff <(find /dir1/ -type f -exec md5sum {} + | sort -k 2) <(find /dir2/ -type f -exec md5sum {} + | sort -k 2)

Se você quiser ver apenas as alterações:

diff <(find /dir1/ -type f -exec md5sum {} + | sort -k 2 | cut -f1 -d" ") <(find /dir2/ -type f -exec md5sum {} + | sort -k 2 | cut -f1 -d" ")

O comando cut imprime somente o hash (primeiro campo) a ser comparado por diff. Caso contrário, o diff imprimirá todas as linhas, pois os caminhos do diretório diferem mesmo quando o hash é o mesmo.

Mas você não sabe qual arquivo foi alterado ...

Para isso, você pode tentar algo como

diff <(find /dir1/ -type f -exec md5sum {} + | sort -k 2 | sed 's/ .*\// /') <(find /dir2/ -type f -exec md5sum {} + | sort -k 2 | sed 's/ .*\// /')

Esta estratégia é muito útil quando os dois diretórios a serem comparados não estão na mesma máquina e você precisa ter certeza de que os arquivos são iguais em ambos os diretórios.

Outra boa maneira de fazer o trabalho é usar o comando diff do Git (pode causar problemas quando os arquivos têm permissões diferentes - > todos os arquivos são listados na saída):

git diff --no-index dir1/ dir2/
    
por Adail Junior 09.01.2017 / 21:05
71

Você pode usar o comando diff da mesma forma que usaria para arquivos:

diff <directory1> <directory2>

Se você quiser ver também as subpastas e os arquivos, use a opção -r :

diff -r <directory1> <directory2>
    
por Alex R. 16.02.2014 / 17:59
22

Por meio de você não está usando bash, você pode fazer isso usando diff com --brief e --recursive :

$ diff -rq dir1 dir2 
Only in dir2: file2
Only in dir1: file1

O man diff inclui as duas opções:

-q, --brief
report only when files differ

-r, --recursive
recursively compare any subdirectories found

    
por Braiam 16.02.2014 / 22:19
13

Aqui está uma alternativa, para comparar apenas nomes de arquivos e não seus conteúdos:

diff <(cd folder1 && find . | sort) <(cd folder2 && find . | sort)

Esta é uma maneira fácil de listar arquivos ausentes, mas é claro que não detectará arquivos com o mesmo nome, mas conteúdo diferente!

(Pessoalmente eu uso meu próprio script diffdirs , mas isso faz parte de uma biblioteca maior .

    
por joeytwiddle 16.02.2014 / 18:35
6

Talvez uma opção seja executar o rsync duas vezes

rsync -r -n -t -v --progress -c -s /dir1/ /dir2/

Com a linha anterior, você obterá arquivos que estão em dir1 e são diferentes (ou ausentes) em dir2. Também pastas com datas diferentes.

rsync -r -n -t -v --progress -c -s /dir2/ /dir1/

O mesmo para dir2

#from the rsync --help :
-r, --recursive             recurse into directories
-n, --dry-run               perform a trial run with no changes made
-t, --times                 preserve modification times
-v, --verbose               increase verbosity
    --progress              show progress during transfer
-c, --checksum              skip based on checksum, not mod-time & size
-s, --protect-args          no space-splitting; only wildcard special-chars

Você pode excluir a opção -n para passar pelas alterações. Isso é copiar a lista de arquivos para a segunda pasta.

Caso você faça isso, talvez seja uma boa opção usar -u, para evitar sobrescrever arquivos mais recentes.

-u, --update                skip files that are newer on the receiver
    
por Ferroao 17.12.2017 / 00:26
3

Se você quiser tornar cada arquivo expansível e flexível, você pode canalizar a saída de diff -r para o Vim.

Primeiro, vamos dar ao Vim uma regra de dobragem:

mkdir -p ~/.vim/ftplugin
echo "set foldexpr=getline(v:lnum)=~'^diff.*'?'>1':1 foldmethod=expr fdc=2" >> ~/.vim/ftplugin/diff.vim

Agora é só:

diff -r dir1 dir2 | vim -

Você pode acessar zo e zc para abrir e fechar dobras. Para sair do Vim, pressione :q<Enter>

    
por joeytwiddle 06.03.2016 / 05:25
3

Tarefa relativamente fácil de realizar em python:

python -c 'import os,sys;d1=os.listdir(sys.argv[1]);d2=os.listdir(sys.argv[2]);d1.sort();d2.sort();x="SAME" if d1 == d2 else "DIFF";print x' DIR1 DIR2

Substitua os valores reais por DIR1 e DIR2 .

Veja o exemplo de execução:

$ python -c 'import os,sys;d1=os.listdir(sys.argv[1]);d2=os.listdir(sys.argv[2]);d1.sort();d2.sort();x="SAME" if d1 == d2 else "DIFF";print x' Desktop/ Desktop
SAME
$ python -c 'import os,sys;d1=os.listdir(sys.argv[1]);d2=os.listdir(sys.argv[2]);d1.sort();d2.sort();x="SAME" if d1 == d2 else "DIFF";print x' Desktop/ Pictures/
DIFF

Para facilitar a leitura, aqui está um script real em vez de uma linha:

#!/usr/bin/env python
import os, sys

d1 = os.listdir(sys.argv[1])
d2 = os.listdir(sys.argv[2])
d1.sort()
d2.sort()

if d1 == d2:
    print("SAME")
else:
    print("DIFF")
    
por Sergiy Kolodyazhnyy 14.11.2016 / 07:12
3

Inspirado na resposta de Sergiy, eu escrevi meu próprio script Python para comparar dois diretórios.

Ao contrário de muitas outras soluções, ele não compara o conteúdo dos arquivos. Também não vai dentro de subdiretórios que estão faltando em um dos diretórios. Portanto, a saída é bastante concisa e o script trabalha rapidamente com diretórios grandes.

#!/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])

Se você salvá-lo em um arquivo chamado compare_dirs.py , poderá executá-lo com o Python3.x:

python3 compare_dirs.py dir1 dir2

Exemplo de saída:

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

P.S. Se você precisar comparar tamanhos de arquivo e hashes de arquivo para possíveis alterações, publiquei um script atualizado aqui: link

    
por Ohne Kleidung 16.01.2018 / 11:01
0

Vou adicionar a esta lista uma alternativa do NodeJs que escrevi há algum tempo.

dir-compare

npm install dir-compare -g
dircompare dir1 dir2
    
por gliviu 20.02.2018 / 21:51