Разница между двумя каталогами в Linux

242

Я пытаюсь найти файлы, существующие в одном каталоге, но не в другом, я попытался использовать эту команду:

diff -q dir1 dir2

Проблема с приведенной выше командой заключается в том, что она находит файлы dir1, но не в dir2, а также файлы в dir2, но не в dir1,

Я пытаюсь найти файлы в dir1, но не только в dir2.

Вот небольшой пример того, что мои данные выглядят как

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

Еще один вопрос, как я могу найти файлы в dir1, но не в dir2 или dir3 в одной команде?

Теги:
diff
command-line

15 ответов

344
Лучший ответ
diff -r dir1 dir2 | grep dir1 | awk '{print $4}' > difference1.txt

Объяснение:

  • diff -r dir1 dir2 показывает, какие файлы находятся только в каталоге dir1 и только в dir2, а также изменения файлов, присутствующих в обоих каталогах, если они есть.

  • diff -r dir1 dir2 | grep dir1 показывает, какие файлы находятся только в dir1

  • awk для печати только имени файла.

  • 3
    Я grep для СТГ , как ^dir1 , чтобы убедиться , что я не получаю dir1 , появляющийся в конце пути.
  • 0
    @Alfe Это может быть улучшено. Я использую $4 в качестве примера. Фактически, на моем настоящем Ubuntu, diff отвечает на итальянском. $4 - это нормально для ответов на итальянском и английском языках, но я не уверен в других языках ...
93

Это должно сделать работу:

diff -rq dir1 dir2

Опции объяснены (с помощью справочной страницы diff (1)):

  • -r - Рекурсивно сравнить все найденные подкаталоги.
  • -q - -q только файлы различаются.
  • 7
    Ницца! Но я думаю, что это должно быть расширено следующим образом: diff -rq dir1 dir2 | grep 'Only in dir1/'
  • 2
    Это сравнение по содержанию, но может занять много времени на медленных дисках.
Показать ещё 2 комментария
43
comm -23 <(ls dir1 |sort) <(ls dir2|sort)

Эта команда даст вам файлы, которые находятся в каталоге dir1, а не в каталоге dir2.

О знаке <( ), вы можете использовать его как "подстановка процесса".

  • 0
    было бы неплохо работать и с подкаталогами, я думаю (ls -R dir1|sort) справится с задачей
  • 1
    Это будет работать в режиме восстановления OS X.
Показать ещё 2 комментария
24

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

Пример:

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

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

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

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

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

diff dir1.txt dir2.txt

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

Другим хорошим способом выполнения задания является использование git

git diff --no-index dir1/ dir2/

С уважением!

  • 0
    Я не пошел git мог сделать diff на произвольных каталогах, которые не находятся в репозитории git ... удивительный !!! Этот ответ только что решил для меня большую проблему, спасибо
10

Meld (http://meldmerge.org/) отлично справляется с сопоставлением каталогов и файлов внутри.

Изображение 3898

  • 0
    За исключением того, что Мелд делает паршивую работу, когда дело доходит до концов строки ...
  • 1
    Никогда не было проблем с окончаниями строк. Можете ли вы подробно?
Показать ещё 2 комментария
10

vim Плагин DirDiff - еще один очень полезный инструмент для сравнения каталогов.

vim -c "DirDiff dir1 dir2"

Он не только перечисляет, какие файлы отличаются между каталогами, но также позволяет вам проверять/изменять с помощью vimdiff файлы, которые отличаются друг от друга.

7

Не удовлетворенный всеми ответами, поскольку большинство из них работают очень медленно и производят излишне длинный вывод для больших каталогов, я написал собственный скрипт 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])

Использование образца:

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

Или если вы хотите видеть только файлы из первого каталога:

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 Если вам нужно сравнить размеры файлов и хэши файлов для потенциальных изменений, я опубликовал обновленный сценарий здесь: https://gist.github.com/amakukha/f489cbde2afd32817f8e866cf4abe779

  • 0
    Достаточно простой скрипт, который делает именно то, что я хотел: Проверить массовую копию: +1 от меня. (хотя для конвертации в python2 необходимо) Подсказка: использование множеств может сделать часть diff проще.
5

Другой (возможно, более быстрый для больших каталогов) подход:

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

Команда sed удаляет первый компонент каталога благодаря сообщению Эрика)

  • 1
    Я полагаю, что этот метод проще (все еще используется find следовательно, комментарий, а не отдельный ответ): cd dir2; find . -exec [ -e ../dir1/{} ] \; -o -print 2>/dev/null Это будет печатать файлы, присутствующие в dir2, но не присутствующие в dir1.
4

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

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

Объяснение:

  • diff -r dir1 dir2: сравнить
  • grep 'Только в': получите строки, содержащие 'Only in'
  • grep dir1: получить строки, содержащие dir
4

Это немного поздно, но может помочь кому-то. Не уверен, что diff или rsync выдают только имена файлов в таком формате. Спасибо пнну за то, что я дал это прекрасное решение, которое я расширил ниже.

Если вам нужны только имена файлов, поэтому просто скопировать нужные файлы в чистом формате, вы можете использовать команду find.

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

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

Если вы просто хотите файлы:

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

Аналогично для каталогов:

comm -23 <(find dir1 -type d | sed 's/dir1/\//'| sort) <(find dir2 -type d | sed 's/dir2/\//'| sort) | sed 's/^\//dir1/'
  • 1
    Обратите внимание, что вы можете сделать cd перед find вместо использования sed , например: comm -23 <(cd dir1 || exit; find -type f | sort) <(cd dir2 || exit; find -type f | sort) . ( exit s здесь, чтобы предотвратить использование find текущим каталогом в случае сбоя cd .)
  • 0
    Также обратите внимание, что ваше решение может завершиться сбоем при наличии файлов с определенными специальными символами, если у вас есть самая последняя версия comm с поддержкой -z (поставляется с git.savannah.gnu.org/cgit/coreutils.git/commit/… ) Вы можете сделать comm -23 -z <(cd dir1 && find -type f -print0 | sort -z) <(cd dir2 && find -type f -print0 | sort -z) . (Тем временем я также понял, что exit s можно заменить.)
0

Этот ответ оптимизирует одно из предложений от @Adail-Junior, добавив параметр -D, что полезно, когда ни один из сравниваемых каталогов не является репозиториями git:

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

Если вы используете -D вы не увидите сравнения с /dev/null: text Binary files a/whatever and/dev/null differ

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

Это сценарий bash для печати команд для синхронизации двух каталогов

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 grep может инвертировать поиск с помощью опции -v. Это позволяет grep сообщать о строках, которые не совпадают. Этим вы можете удалить файлы из dir2 из списка файлов в каталоге dir1.

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

Опции -F -x сообщают grep выполнить строковый поиск по всей строке.

0

kdiff3 имеет хороший интерфейс diff для файлов и каталогов.

Он работает на Windows, Linux и MacOS.

Вы можете установить его несколькими способами:

0

Упрощенный способ сравнения двух каталогов с использованием команды DIFF

diff filename.1 filename.2 > filename.dat → Введите

открыть файл filename.dat после завершения выполнения

и вы увидите: Только в filename.1: filename.2 Только в: directory_name: name_of_file1 Только в: directory_Name: name_of_file2

  • 0
    Почему вы должны выводить в файл .dat?

Ещё вопросы

Сообщество Overcoder
Наверх
Меню