Сравнение содержимого двух каталогов

48

У меня есть два каталога, которые должны содержать одни и те же файлы и иметь одну и ту же структуру каталогов.

Я думаю, что что-то отсутствует в одном из этих каталогов.

Используя оболочку bash, есть ли способ сравнить мои каталоги и посмотреть, отсутствует ли в одном из них файлы, которые присутствуют в другом?

    
задан AndreaNobili 16.02.2014 в 17:54
источник

9 ответов

28

Хорошим способом сделать это сравнение является использование find с md5sum , затем diff .

Пример

Используйте find, чтобы перечислить все файлы в каталоге, а затем вычислить хеш md5 для каждого файла и отсортировать его по имени файла в файл:

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

Сделайте ту же процедуру в другом каталоге:

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

Затем сравните результат с двумя файлами с diff :

diff -u dir1.txt dir2.txt

Или как одна команда, использующая замену процесса:

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

Эта стратегия очень полезна, когда два сопоставимых каталога не находятся на одном компьютере, и вам нужно убедиться, что файлы одинаковы в обоих каталогах.

Еще один хороший способ выполнить эту работу - использовать команду diff Git:

git diff --no-index dir1/ dir2/
    
ответ дан Adail Junior 09.01.2017 в 21:05
источник
49

Вы можете использовать команду diff так же, как использовать ее для файлов:

diff <directory1> <directory2>

Если вы хотите видеть вложенные папки и файлы, вы можете использовать опцию -r :

diff -r <directory1> <directory2>
    
ответ дан Alex R. 16.02.2014 в 17:59
13

Вот альтернатива, чтобы сравнивать только имена файлов, а не их содержимое:

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

Это простой способ перечислить недостающие файлы, но, конечно, не будет обнаруживать файлы с тем же именем, но с другим содержимым!

(Лично я использую собственный скрипт diffdirs , но это часть более крупной библиотеки .)

    
ответ дан joeytwiddle 16.02.2014 в 18:35
10

Через вы не используете bash, вы можете сделать это, используя diff с --brief и --recursive :

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

В man diff включены обе опции:

  

-q , --brief
                сообщать только, когда файлы отличаются

     

-r , --recursive
                рекурсивно сравнить любые найденные подкаталоги

    
ответ дан Braiam 16.02.2014 в 22:19
3

Если вы хотите сделать каждый файл расширяемым и сворачиваемым, вы можете отправить вывод diff -r в Vim.

Сначала дадим Vim правило складывания:

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

Теперь просто:

diff -r dir1 dir2 | vim -

Вы можете нажать zo и zc , чтобы открыть и закрыть складки. Чтобы выйти из Vim, нажмите :q<Enter>

    
ответ дан joeytwiddle 06.03.2016 в 05:25
2

Довольно простая задача для достижения в 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

Заменить фактические значения для DIR1 и DIR2 .

Вот пример прогона:

$ 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

Для удобства чтения здесь вместо скрипта используется реальный скрипт:

#!/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")
    
ответ дан Sergiy Kolodyazhnyy 14.11.2016 в 07:12
1

Вдохновленный ответом Сергея, я написал собственный скрипт Python для сравнения двух каталогов.

В отличие от многих других решений, он не сравнивает содержимое файлов. Кроме того, он не входит в подкаталоги, отсутствующие в одном из каталогов. Таким образом, вывод довольно краткий, и скрипт работает быстро с большими каталогами.

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

Если вы сохраните его в файле с именем compare_dirs.py , вы можете запустить его с помощью Python3.x:

python3 compare_dirs.py dir1 dir2

Пример вывода:

[email protected]:~$ 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
    
ответ дан Andriy Makukha 16.01.2018 в 11:01
0

Возможно, один из вариантов - запустить rsync два раза

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

В предыдущей строке вы получите файлы, которые находятся в каталоге dir1, и отличаются (или отсутствуют) в dir2. Также папки с другой датой.

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

То же самое для 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

Вы можете удалить параметр -n, чтобы претерпеть изменения. Это копирование списка файлов во вторую папку.

В случае, если вы это сделаете, может быть, хороший вариант - использовать -u, чтобы не перезаписывать более новые файлы.

-u, --update                skip files that are newer on the receiver
    
ответ дан Ferroao 17.12.2017 в 00:26
0

Я добавлю в этот список альтернативу NodeJs, которую я написал некоторое время назад.

dir-compare

npm install dir-compare -g
dircompare dir1 dir2
    
ответ дан gliviu 20.02.2018 в 21:51