Знайдіть файли, які існують в одній директорії, а не в іншій [закрито]


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

Відповіді:


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 друкувати лише ім'я файлу.


5
Я б grepхотів, ^dir1щоб переконатися, що я не dir1з’являться пізніше на шляху.
Алфе

@Alfe Це можна покращити. Я використовую $4як приклад. Насправді, на мою фактичну Ubuntu, diffвідповіді італійською мовою. $4добре для італійських та англійських відповідей, але я не впевнений у всіх інших мовах ...
asclepix

139

Це має зробити цю роботу:

diff -rq dir1 dir2

Параметри, що пояснюються (через розкладну (1) чоловічу сторінку ):

  • -r - рекурсивно порівнюйте знайдені підкаталоги.
  • -q - Виводить лише те, чи відрізняються файли.

8
Приємно! Але я думаю, що це слід продовжити так:diff -rq dir1 dir2 | grep 'Only in dir1/'
sobi3ch

2
Це порівняння за змістом, але може тривати довгий час на повільних дисках.
Smeterlink

5
Лише зауваження щодо -qопції: На сторінках "man" вказано лише "Вивести лише те, чи файли відрізняються", а не те, як вони перевіряють, чи вони різні. Я вивчив вихідний код і виявив, що він перевіряє лише розміри файлів, щоб визначити відмінності, а не фактичний вміст.
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.

Щодо <( )знака, ви можете погуглювати його як "процес заміни".


було б (ls -R dir1|sort)
непогано

1
Це буде працювати в режимі відновлення OS X.
Ентоні Вановер

@ulkas, якщо ви використовуєте, вихід може бути неправильним (ls -R dir|sort).
Андрій Макуха

3
vimdiff забезпечує набагато приємніше візуальне порівняння з виділенням кольорів:vimdiff <(ls dir1 |sort) <(ls dir2|sort)
Логан Рід

32

Хороший спосіб зробити це порівняння - використовувати findз md5sum, потім a 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/

З повагою!


1
Я не пішов git міг зробити розбіжність у довільних каталогах, які не знаходяться всередині git repo ... приголомшливо !!! Ця відповідь просто вирішила для мене велику проблему, дякую
ViktorNova

17

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

Meld порівняння каталогів


За винятком того, що Мельд виконує тугу роботу, коли йдеться про закінчення рядків ...
0xC0000022L

1
Ніколи не було проблем із закінченнями рядків. Чи можете ви детально?
Каталін Гріцу

Так, це не вказує закінчення рядка. Це (неодноразово) призводило до того, що розробники, використовуючи цей інструмент, здійснювали зміни, які "фіксували" закінчення рядка, наприклад, перетворюючи CRLF у CRLFLF.
0xC0000022L

3
Він також наполягає на тому, щоб читати вміст файлів, і тому майже непридатний з каталогами >> 1 Гб.
Томіслав Накіч-Альфіревич

13

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

vim -c "DirDiff dir1 dir2"

Він не тільки перераховує, які файли відрізняються між каталогами, але також дозволяє перевіряти / змінювати за допомогою vimdiff файли, які відрізняються.


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Команда видаляє перший компонент каталогу завдяки Erik`s повідомленням )


1
Я вважаю, що цей метод простіший (все-таки використовуючи findкоментар, а не окрему відповідь): cd dir2; find . -exec [ -e ../dir1/{} ] \; -o -print 2>/dev/null Це буде друкувати файли, присутні в dir2, але не в dir1.
Олександр Амелкін

5

Це трохи пізно, але може комусь допомогти. Не впевнений, чи розрізнення або 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). (Ось exitтут, щоб запобігти findвикористанню поточного каталогу, не cdвдасться.)
phk

Також зауважте, що ваше рішення може вийти з ладу при наявності файлів з певними спеціальними символами, якщо у вас є остання версія 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 "Тільки в": отримуйте рядки, які містять "Тільки в"
  • 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 >> Введіть

відкрити filename.dat після завершення запуску

і ви побачите: Тільки в ім'я файлу.1: ім'я файлу.2 Тільки в: ім'я каталогу: ім'я_файла_файла1 Лише в: каталог_ім'я: ім’я_фа_файлу2


Чому вам потрібно вивести файл у .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на пошук рядка по всьому рядку.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.