Найти файлы, существующие в одном каталоге, но не в другом [закрыто]

295

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

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в одной команде?

Ошибка 404
источник

Ответы:

390
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 распечатать только имя файла.

asclepix
источник
5
Я бы grepхотел ^dir1убедиться, что не dir1появлюсь позже на этом пути.
Alfe
@Alfe Это может быть улучшено. Я использую $4в качестве примера. На самом деле, на моем настоящем Ubuntu, diffотвечает на итальянском. $4подходит для ответов на итальянском и английском языках, но я не уверен в других языках ...
asclepix
139

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

diff -rq dir1 dir2

Опции объяснены (через справочную страницу diff (1) ):

  • -r - Рекурсивно сравнивать любые найденные подкаталоги.
  • -q - Выводить только файлы отличаются.
tokhi
источник
8
Ницца! Но я думаю, что это должно быть расширено так:diff -rq dir1 dir2 | grep 'Only in dir1/'
sobi3ch
2
Это сравнение по содержанию, но может занять много времени на медленных дисках.
Smeterlink
5
Просто примечание об этой -qопции: на страницах руководства написано только «Вывести только, если файлы различаются», а не то, как он проверяет, отличаются ли они. Я просмотрел исходный код и обнаружил, что он проверяет только размеры файлов, чтобы определить различия, а не фактическое содержимое.
ryancdotnet
Что касается -qопции, я не могу воспроизвести, что она только проверяет размер файла. Использование GNU Diffutils 3.7 для сравнения двух файлов с одинаковым размером, но разным содержимым с diff -q file1 file2выходными данными Files file1 and file2 differ.
Стефан Шмидт
50
comm -23 <(ls dir1 |sort) <(ls dir2|sort)

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

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

plhn
источник
было бы (ls -R dir1|sort)
неплохо
1
Это будет работать в режиме восстановления OS X
Энтони Вановер
@ulkas, вывод может быть неправильным, если вы используете (ls -R dir|sort).
Андрей Макуха
3
vimdiff обеспечивает гораздо более приятное визуальное сравнение с выделением цветом:vimdiff <(ls dir1 |sort) <(ls dir2|sort)
Логан Рид
32

Хороший способ сделать это сравнение - использовать 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/

С уважением!

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

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

Мелд, сравнивающий каталоги

Каталин Хриту
источник
За исключением того, что Мелд делает паршивую работу, когда дело доходит до
концов
1
Никогда не было проблем с окончаниями строк. Можете ли вы подробно?
Каталин Хритку
Да, это не означает окончания строки. Это (неоднократно) приводило к тому, что разработчики, использующие этот инструмент, вносили изменения, которые «исправляли» окончания строк, например, превращая CRLF в CRLFLF.
0xC0000022L
3
Он также настаивает на чтении содержимого файла и поэтому почти бесполезен с >> 1GB каталогами.
Томислав Накич-Альфиревич
13

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

vim -c "DirDiff dir1 dir2"

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

drrossum
источник
11

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

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

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

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

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

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

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

Если вам нужны только имена файлов, поэтому легко скопировать нужные файлы в чистом формате, вы можете использовать команду 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). ( exitS здесь, чтобы предотвратить findиспользование текущего каталога, должен cd
произойти
Также обратите внимание, что ваше решение может дать сбой при наличии файлов с определенными специальными символами, если у вас есть самая последняя версия 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), (Тем временем я также понял, что exits можно заменить.)
phk
5

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

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

Объяснение:

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

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

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

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

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

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

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

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

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

nerakk
источник
Почему вы должны выводить в файл .dat?
Вишну НК,
1

Это скрипт 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выполнить поиск строки по всей строке.

ceving
источник