Membandingkan isi dua direktori

93

Saya memiliki dua direktori yang harus berisi file yang sama dan memiliki struktur direktori yang sama.

Saya pikir ada sesuatu yang hilang di salah satu direktori ini.

Menggunakan bash shell, adakah cara untuk membandingkan direktori saya dan melihat apakah salah satu dari mereka ada file yang hilang di yang lain?

AndreaNobili
sumber
1
Apa output dari bash --version?
Pekerjaan di
1
Serupa tetapi lebih spesifik: stackoverflow.com/questions/16787916/…
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

Jawaban:

64

Cara yang baik untuk melakukan perbandingan ini adalah dengan menggunakan finddengan md5sum, maka diff.

Contoh

Gunakan find untuk membuat daftar semua file di direktori kemudian menghitung hash md5 untuk setiap file dan pipa itu diurutkan berdasarkan nama file ke file:

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

Lakukan prosedur yang sama ke direktori lain:

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

Kemudian bandingkan hasil dua file dengan diff:

diff -u dir1.txt dir2.txt

Atau sebagai perintah tunggal menggunakan proses substitusi:

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

Jika Anda hanya ingin melihat perubahan:

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" ")

Perintah cut hanya mencetak hash (bidang pertama) untuk dibandingkan dengan diff. Jika tidak, diff akan mencetak setiap baris karena jalur direktori berbeda walaupun hashnya sama.

Tetapi Anda tidak akan tahu file mana yang berubah ...

Untuk itu, Anda dapat mencoba sesuatu seperti

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

Strategi ini sangat berguna ketika dua direktori yang akan dibandingkan tidak berada di mesin yang sama dan Anda perlu memastikan bahwa file-file tersebut sama di kedua direktori.

Cara lain yang baik untuk melakukan pekerjaan itu adalah menggunakan diffperintah Git (dapat menyebabkan masalah ketika file memiliki izin yang berbeda -> setiap file terdaftar dalam output kemudian):

git diff --no-index dir1/ dir2/
Adail Junior
sumber
1
Ini tidak berfungsi tanpa langkah penyortiran ekstra, karena urutan finddaftar file akan berbeda secara umum antara dua direktori.
Faheem Mitha
1
Seseorang dapat menggunakan metode yang dijelaskan dalam askubuntu.com/a/662383/15729 untuk mengurutkan file.
Faheem Mitha
1
Saya mendapatkan kesalahan `` find: md5sum: Tidak ada file atau direktori
Houman
1
@ Manusia Saya tidak tahu apa Distro Linux yang Anda gunakan, tapi mungkin Anda perlu menginstal paket yang akan menyediakan de md5sum. Di Fedora 26 Anda dapat menginstalnya dengan: #dnf install coreutils
Junior
Gunakan md5 () sebagai gantinya
boj
81

Anda dapat menggunakan diffperintah seperti halnya Anda menggunakannya untuk file:

diff <directory1> <directory2>

Jika Anda ingin melihat subfolder dan -file juga, Anda dapat menggunakan -ropsi:

diff -r <directory1> <directory2>
Alex R.
sumber
2
Tidak tahu diffberfungsi untuk direktori juga (man diff mengkonfirmasi hal itu), tetapi ini tidak secara rekursif memeriksa perubahan dalam subdirektori di dalam subdirektori.
jobin
1
@ Jonob Itu aneh ... Bagi saya, itu berhasil.
Alex R.
1
Saya memiliki sesuatu seperti ini: a/b/c/d/a, x/b/c/d/b. Lihat apa yang diff a xmemberi Anda.
jobin
2
Anda harus menggunakan -ropsi ini. Itu ( diff -r a x) memberi saya:Only in a/b/c/d: a. only in x/b/c/d: b.
Alex R.
3
beda tunjukkan bedanya dengan file INTO tetapi tidak jika suatu direktori berisi file yang tidak berisi yang lainnya !!! Saya tidak perlu tahu perbedaan dalam file tetapi juga jika ada file dalam direktori dan tidak dalam yang lain
AndreaNobili
25

Melalui Anda tidak menggunakan bash, Anda dapat melakukannya menggunakan diff dengan --briefdan --recursive:

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

The man difftermasuk kedua pilihan:

-q, --brief
laporkan hanya ketika file berbeda

-r, --recursive
secara rekursif membandingkan subdirektori yang ditemukan

Braiam
sumber
13

Berikut ini adalah alternatif, untuk membandingkan hanya nama file, dan bukan isinya:

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

Ini adalah cara mudah untuk membuat daftar file yang hilang, tetapi tentu saja tidak akan mendeteksi file dengan nama yang sama tetapi isinya berbeda!

(Secara pribadi saya menggunakan diffdirsskrip saya sendiri , tetapi itu adalah bagian dari perpustakaan yang lebih besar .)

joeytwiddle
sumber
3
Anda sebaiknya menggunakan proses substitusi, bukan temp file ...
mniip
3
Perhatikan bahwa ini tidak mendukung nama file dengan karakter khusus tertentu, dalam hal ini Anda mungkin ingin menggunakan pembatas nol yang AFAIK difftidak mendukung seperti yang sekarang. Tetapi ada commyang mendukungnya sejak git.savannah.gnu.org/cgit/coreutils.git/commit/... jadi setelah sampai pada coreutil di dekat Anda, Anda dapat melakukannya comm -z <(cd folder1 && find -print0 | sort) <(cd folder2 && find -print0 | sort -z)(yang hasilnya mungkin harus Anda konversi lebih jauh dalam format Anda perlu menggunakan --output-delimiterparameter dan alat tambahan).
phk
8

Mungkin satu opsi adalah menjalankan rsync dua kali:

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

Dengan baris sebelumnya, Anda akan mendapatkan file yang ada di dir1 dan berbeda (atau tidak ada) di dir2.

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

Sama untuk 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
-O, --omit-dir-times        omit directories from --times

Anda dapat menghapus -nopsi untuk menjalani perubahan. Yaitu menyalin daftar file ke folder kedua.

Jika Anda melakukannya, mungkin pilihan yang baik adalah menggunakan -u, untuk menghindari menimpa file yang lebih baru.

-u, --update                skip files that are newer on the receiver

Satu kalimat:

rsync -rtvcsOu -n --progress /dir1/ /dir2/ && rsync -rtvcsOu -n --progress /dir2/ /dir1/
Ferroao
sumber
3

Jika Anda ingin membuat setiap file diperluas dan dapat dilipat, Anda dapat menyalurkan output diff -rke Vim.

Pertama mari kita beri Vim aturan lipat:

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

Sekarang hanya:

diff -r dir1 dir2 | vim -

Anda dapat menekan zodan zcmembuka dan menutup lipatan. Untuk keluar dari Vim, tekan:q<Enter>

joeytwiddle
sumber
3

Tugas yang cukup mudah untuk dicapai dengan 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

Mengganti nilai aktual untuk DIR1dan DIR2.

Berikut contoh dijalankan:

$ 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

Agar mudah dibaca, inilah skrip aktual alih-alih satu baris:

#!/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
sumber
2
Perhatikan bahwa os.listdirtidak memberikan urutan tertentu. Jadi daftar mungkin memiliki hal yang sama dalam urutan berbeda dan perbandingannya akan gagal.
muru
1
@muru poin bagus, saya akan memasukkan penyortiran untuk itu
Sergiy Kolodyazhnyy
3

Terinspirasi oleh balasan Sergiy, saya menulis skrip Python saya sendiri untuk membandingkan dua direktori.

Tidak seperti banyak solusi lain, itu tidak membandingkan konten file. Juga tidak masuk ke dalam subdirektori yang tidak ada di salah satu direktori. Jadi hasilnya cukup ringkas dan skrip bekerja cepat dengan direktori besar.

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

Jika Anda menyimpannya ke file bernama compare_dirs.py, Anda bisa menjalankannya dengan Python3.x:

python3 compare_dirs.py dir1 dir2

Output sampel:

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 Jika Anda perlu membandingkan ukuran file dan hash file untuk kemungkinan perubahan, saya menerbitkan skrip yang diperbarui di sini: https://gist.github.com/amakukha/f489cbde2afd32817f8e866cf4abe779

Andriy Makukha
sumber
1
Terima kasih, saya menambahkan regexp param ketiga opsional untuk melewati / abaikan gist.github.com/mscalora/e86e2bbfd3c24a7c1784f3d692b1c684 untuk membuat apa yang saya butuhkan seperti:cmpdirs dir1 dir2 '/\.git/'
Mike
0

Saya akan menambahkan daftar ini alternatif NodeJs yang saya tulis beberapa waktu lalu.

dir-bandingkan

npm install dir-compare -g
dircompare dir1 dir2
gliviu
sumber
0

Saya ingin menyarankan alat hebat yang baru saja saya temukan: MELD .

Ini bekerja dengan baik dan semua yang dapat Anda lakukan dengan perintah diffpada sistem berbasis Linux, dapat direplikasi dengan antarmuka grafis yang bagus! Nikmati

Leos313
sumber