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

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

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

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

168
задан 28 April 2015 в 02:53

13 ответов

Хороший способ провести это сравнение - использовать 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 <(find /dir1/ -type f -exec md5sum {} + | sort -k 2 | cut -f1 -d" ") <(find /dir2/ -type f -exec md5sum {} + | sort -k 2 | cut -f1 -d" ")

Команда cut печатает только хэш (первое поле) в сравнивать по разн. В противном случае diff будет печатать каждую строку, так как пути к каталогам различаются, даже если хэш одинаковый.

Но вы не узнаете, какой файл был изменен ...

Для этого вы можете попробовать что-то вроде

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

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

Еще один хороший способ выполнить эту работу - использовать Git diff (может вызвать проблемы, если файлы имеют разные разрешения -> тогда каждый файл будет указан в выводе):

git diff --no-index dir1/ dir2/
117
ответ дан 28 April 2015 в 02:53

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

diff <directory1> <directory2>

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

diff -r <directory1> <directory2>
132
ответ дан 28 April 2015 в 02:53

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

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

В man diff включает в себя обе опции:

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

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

49
ответ дан 28 April 2015 в 02:53

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

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

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

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

14
ответ дан 28 April 2015 в 02:53

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

rsync -rtOvcs --progress -n /dir1/ /dir2/

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

rsync -rtOvcs --progress -n /dir2/ /dir1/

То же самое для dir2

#from the rsync --help :
-n, --dry-run               perform a trial run with no changes made

-r, --recursive             recurse into directories
-t, --times                 preserve modification times
-O, --omit-dir-times        omit directories from --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

Однострочный файл. :

rsync -rtOvcsu --progress -n  /dir1/ /dir2/ && rsync -rtOvcsu --progress -n /dir2/ /dir1/
18
ответ дан 28 April 2015 в 02:53

Если вы хотите сделать каждый файл расширяемым и сворачиваемым, вы можете направить вывод команды 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 - -R

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

-R не является обязательным, но я считаю его полезным вместе с - , потому что он не дает Vim подслушивать вас. для сохранения буфера при выходе.

4
ответ дан 28 April 2015 в 02:53

Достаточно простая задача, которую можно выполнить в 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")
4
ответ дан 28 April 2015 в 02:53

Вдохновленный ответом Сергея, я написал собственный скрипт 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

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

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

PS Если вам нужно сравнить размеры файлов и хэши файлов на предмет возможных изменений, я опубликовал обновленный скрипт здесь: https://gist.github.com/amakukha/f489cbde2afd32817f8e866cf4abe779

4
ответ дан 28 April 2015 в 02:53

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

dir-compare

npm install dir-compare -g
dircompare dir1 dir2
0
ответ дан 28 April 2015 в 02:53

Хороший ответ Адала Младшего может иметь проблемы со временем выполнения, если у вас есть сотни тысяч файлов! Вот еще один способ сделать это. Скажем, вы хотите сравнить все имена файлов папки A со всеми именами файлов папки B. Шаг 1, cd в папку A и выполните:

find . | sort -k 2 > listA.txt

Шаг 2, cd в папку B и выполните:

find . | sort -k 2 > listB.txt

Шаг 3, возьмите разницу listA.txt и listB.txt

Я пробовал это в папках, содержащих половину миллион файлов txt, и менее чем за 30 секунд на моем экране появилось различие, тогда как вычисление сумм md5, а затем конвейерная обработка и последующее добавление могут занять очень-очень много времени. Обратите внимание, что в исходном вопросе предлагается сравнить имена файлов (а не их содержимое!) И проверить, отсутствуют ли файлы между сравниваемыми папками! Спасибо

1
ответ дан 22 November 2019 в 23:07

Я хотел бы предложить отличный инструмент, который я только что обнаружил: MELD .

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

Например, сравнение каталогов простое:

directories comparison

, а также сравнение файлов упрощено:

files comparison

Имеется хорошая интеграция с некоторыми контрольными версиями (например, Git), и их можно использовать как инструмент слияния . См. Полную документацию на его веб-сайте.

5
ответ дан 22 November 2019 в 23:07

Вы можете использовать этот инструмент:

https://github.com/jfabaf/comparefolders/

Я разработал его несколько лет назад, потому что у меня была такая же проблема.

Он сравнивает MD5 файлов, поэтому имена файлов не имеют значения.

0
ответ дан 5 January 2021 в 23:12

Как уже отмечалось, вы также можете использовать команду comm, например таким образом:

comm -3 <(ls -1 dir1) <(ls -1 dir2)

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

0
ответ дан 5 January 2021 в 23:12

Другие вопросы по тегам:

Похожие вопросы: