So sánh nội dung của hai thư mục


92

Tôi có hai thư mục nên chứa cùng một tệp và có cùng cấu trúc thư mục.

Tôi nghĩ rằng một cái gì đó bị thiếu trong một trong những thư mục này.

Sử dụng bash shell, có cách nào để so sánh các thư mục của tôi và xem liệu một trong số chúng có thiếu các tệp có trong tệp kia không?


1
Đầu ra của là bash --versiongì?
việc

1
Tương tự nhưng cụ thể hơn: stackoverflow.com/questions/16787916/
Kẻ

Câu trả lời:


63

Một cách tốt để làm so sánh này là sử dụng findvới md5sum, sau đó a diff.

Thí dụ

Sử dụng find để liệt kê tất cả các tệp trong thư mục, sau đó tính toán băm md5 cho mỗi tệp và đường ống được sắp xếp theo tên tệp vào một tệp:

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

Làm thủ tục tương tự với thư mục khác:

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

Sau đó so sánh kết quả hai tệp với diff:

diff -u dir1.txt dir2.txt

Hoặc như một lệnh duy nhất sử dụng thay thế quá trình:

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

Nếu bạn chỉ muốn xem các thay đổi:

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

Lệnh cut chỉ in hàm băm (trường đầu tiên) được so sánh bằng diff. Nếu không, diff sẽ in mọi dòng vì các đường dẫn thư mục khác nhau ngay cả khi hàm băm giống nhau.

Nhưng bạn sẽ không biết tập tin nào đã thay đổi ...

Đối với điều đó, bạn có thể thử một cái gì đó như

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

Chiến lược này rất hữu ích khi hai thư mục được so sánh không nằm trong cùng một máy và bạn cần đảm bảo rằng các tệp bằng nhau trong cả hai thư mục.

Một cách tốt khác để thực hiện công việc là sử dụng difflệnh của Git (có thể gây ra sự cố khi các tệp có các quyền khác nhau -> sau đó mọi tệp được liệt kê trong đầu ra):

git diff --no-index dir1/ dir2/

1
Điều này không hoạt động mà không có một bước sắp xếp bổ sung, bởi vì thứ tự findsẽ liệt kê các tệp sẽ khác nhau nói chung giữa hai thư mục.
Faheem Mitha

1
Người ta có thể sử dụng phương pháp được mô tả trong Askubfox.com/a/662383/15729 để sắp xếp các tệp.
Faheem Mitha

1
Tôi gặp lỗi `` find: md5sum: Không có tệp hoặc thư mục như vậy
Houman

1
@Houman Tôi không biết Linux Distro bạn đang sử dụng là gì, nhưng có lẽ bạn cần cài đặt gói sẽ cung cấp de md5sum. Trong Fedora 26, bạn có thể cài đặt nó với: #dnf cài đặt coreutils
Adail Junior

Sử dụng md5 () thay vào đó
boj

81

Bạn có thể sử dụng difflệnh giống như bạn sẽ sử dụng nó cho các tệp:

diff <directory1> <directory2>

Nếu bạn muốn xem các thư mục con và -files nữa, bạn có thể sử dụng -rtùy chọn:

diff -r <directory1> <directory2>

2
Không biết cũng diffhoạt động cho các thư mục (man diff xác nhận điều đó), nhưng điều này không kiểm tra đệ quy các thay đổi trong thư mục con bên trong các thư mục con.
việc

1
@Jobin Thật lạ ... Đối với tôi, nó hoạt động.
Alex R.

1
Tôi có một cái gì đó như thế này: a/b/c/d/a, x/b/c/d/b. Xem những gì diff a xmang lại cho bạn.
việc

2
Bạn phải sử dụng -rtùy chọn. Điều đó ( diff -r a x) mang lại cho tôi:Only in a/b/c/d: a. only in x/b/c/d: b.
Alex R.

3
diff cho tôi thấy sự khác biệt của các tệp INTO nhưng không phải nếu một thư mục chứa một tệp mà tệp kia không chứa !!! Tôi không cần biết sự khác biệt trong tệp mà còn nếu một tệp tồn tại trong một thư mục chứ không phải trong một thư mục khác
AndreaNobili

25

Thông qua bạn không sử dụng bash, bạn có thể thực hiện bằng cách sử dụng diff với --brief--recursive:

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

Các man diffbao gồm cả hai lựa chọn:

-q, --brief
chỉ báo cáo khi các tệp khác nhau

-r, --recursive
so sánh đệ quy bất kỳ thư mục con nào được tìm thấy


13

Đây là một cách thay thế, để so sánh chỉ tên tệp chứ không phải nội dung của chúng:

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

Đây là một cách dễ dàng để liệt kê các tệp bị thiếu, nhưng tất nhiên nó sẽ không phát hiện các tệp có cùng tên nhưng nội dung khác nhau!

(Cá nhân tôi sử dụng diffdirstập lệnh của riêng mình , nhưng đó là một phần của thư viện lớn hơn .)


3
Bạn nên sử dụng thay thế quá trình, không phải tệp tạm thời ...
mniip

3
Lưu ý rằng điều này không hỗ trợ tên tệp với một số ký tự đặc biệt, trong trường hợp đó bạn có thể muốn sử dụng các dấu phân cách bằng 0 mà AFAIK diffhiện không hỗ trợ. Nhưng có commcái nào hỗ trợ nó vì git.savannah.gnu.org/cgit/coreutils.git/commit/ít vì vậy một khi nó đến một coreutils gần bạn, bạn có thể làm comm -z <(cd folder1 && find -print0 | sort) <(cd folder2 && find -print0 | sort -z)(đầu ra mà bạn có thể phải chuyển đổi theo định dạng bạn cần sử dụng --output-delimitertham số và các công cụ bổ sung).
phk

7

Có thể một tùy chọn là chạy rsync hai lần:

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

Với dòng trước, bạn sẽ nhận được các tệp trong dir1 và khác (hoặc thiếu) trong dir2.

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

Tương tự cho 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

Bạn có thể xóa -ntùy chọn để trải qua các thay đổi. Đó là sao chép danh sách các tập tin vào thư mục thứ hai.

Trong trường hợp bạn làm điều đó, có thể một lựa chọn tốt là sử dụng -u, để tránh ghi đè lên các tệp mới hơn.

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

Một lớp lót:

rsync -rtvcsOu -n --progress /dir1/ /dir2/ && rsync -rtvcsOu -n --progress /dir2/ /dir1/

3

Nếu bạn muốn làm cho mỗi tệp có thể mở rộng và thu gọn được, bạn có thể chuyển đầu ra của diff -rthành Vim.

Trước tiên, hãy cho Vim một quy tắc gấp:

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

Bây giờ chỉ cần:

diff -r dir1 dir2 | vim -

Bạn có thể nhấn zozcđể mở và đóng nếp gấp. Để thoát khỏi Vim, đánh:q<Enter>


3

Nhiệm vụ khá dễ dàng để đạt được trong 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

Thay thế giá trị thực tế cho DIR1DIR2.

Đây là mẫu chạy:

$ 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

Để dễ đọc, đây là một tập lệnh thực tế thay vì một tập lệnh:

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

2
Lưu ý rằng os.listdirkhông đưa ra bất kỳ thứ tự cụ thể. Vì vậy, các danh sách có thể có những điều tương tự theo thứ tự khác nhau và so sánh sẽ thất bại.
muru

1
@muru điểm tốt, tôi sẽ bao gồm sắp xếp theo đó
Sergiy Kolodyazhnyy

3

Lấy cảm hứng từ câu trả lời của Sergiy, tôi đã viết kịch bản Python của riêng mình để so sánh hai thư mục.

Không giống như nhiều giải pháp khác, nó không so sánh nội dung của các tệp. Ngoài ra, nó không đi vào các thư mục con bị thiếu trong một trong các thư mục. Vì vậy, đầu ra khá súc tích và kịch bản hoạt động nhanh với các thư mục lớn.

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

Nếu bạn lưu nó vào một tệp có tên compare_dirs.py, bạn có thể chạy nó với Python3.x:

python3 compare_dirs.py dir1 dir2

Đầu ra mẫu:

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 Nếu bạn cần phải so sánh kích thước tập tin và tập tin băm cho những thay đổi tiềm năng, tôi xuất bản một kịch bản cập nhật tại đây: https://gist.github.com/amakukha/f489cbde2afd32817f8e866cf4abe779


1
Cảm ơn, tôi đã thêm một regrec tùy chọn thứ ba tùy chọn để bỏ qua / bỏ qua gist.github.com/mscalora/e86e2bbfd3c24a7c1784f3d692b1c684 để thực hiện những gì tôi cần như:cmpdirs dir1 dir2 '/\.git/'
Mike

0

Tôi sẽ thêm vào danh sách này một giải pháp thay thế NodeJ mà tôi đã viết cách đây một thời gian.

so sánh dir

npm install dir-compare -g
dircompare dir1 dir2

0

Tôi muốn đề xuất một công cụ tuyệt vời mà tôi vừa khám phá: MELD .

Nó hoạt động đúng và mọi thứ bạn có thể làm với lệnh difftrên hệ thống dựa trên Linux, có thể được sao chép với Giao diện đồ họa đẹp! Thưởng thức

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.