Tìm các tệp hiện có trong một thư mục nhưng không phải trong [đóng] khác


295

Tôi đang cố gắng tìm các tệp hiện có trong một thư mục nhưng không phải trong thư mục khác, tôi đã thử sử dụng lệnh này:

diff -q dir1 dir2

Vấn đề với lệnh trên là nó tìm thấy cả các tệp trong dir1nhưng không trong dir2cũng như các tệp trong dir2nhưng không trong dir1,

Tôi đang cố gắng tìm các tập tin trong dir1nhưng không dir2chỉ trong .

Đây là một mẫu nhỏ về dữ liệu của tôi trông như thế nào

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

Một câu hỏi khác trong đầu tôi là làm thế nào tôi có thể tìm thấy các tệp trong dir1nhưng không phải trong dir2hoặc dir3trong một lệnh?

Câu trả lời:


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

Giải trình:

  • diff -r dir1 dir2 hiển thị những tệp nào chỉ có trong dir1 và những tệp nào chỉ trong dir2 và cả những thay đổi của các tệp có trong cả hai thư mục nếu có.

  • diff -r dir1 dir2 | grep dir1 cho thấy tập tin nào chỉ có trong dir1

  • awk để chỉ in tên tệp.


5
Tôi muốn grepsth muốn ^dir1đảm bảo rằng tôi sẽ không dir1xuất hiện sau này trên đường dẫn.
Alfe

@ Alfe Nó có thể được cải thiện. Tôi sử dụng $4như một ví dụ. Trong thực tế, trên Ubuntu thực tế của tôi, difftrả lời bằng tiếng Ý. $4trả lời bằng tiếng Ý và tiếng Anh, nhưng tôi không chắc đối với mọi ngôn ngữ khác ...
asclepix

139

Điều này sẽ làm công việc:

diff -rq dir1 dir2

Các tùy chọn được giải thích (thông qua diff (1) trang man ):

  • -r - So sánh đệ quy bất kỳ thư mục con nào được tìm thấy.
  • -q - Chỉ xuất ra cho dù các tập tin khác nhau.

8
Đẹp! Nhưng tôi nghĩ nó nên được mở rộng như thế:diff -rq dir1 dir2 | grep 'Only in dir1/'
sobi3ch

2
Đây là so sánh theo nội dung, nhưng có thể mất nhiều thời gian trên các ổ đĩa chậm.
Smeterlink

5
Chỉ cần một lưu ý về -qtùy chọn: Các trang hướng dẫn chỉ nói "Chỉ xuất ra các tệp khác nhau" chứ không phải cách kiểm tra nếu chúng khác nhau. Tôi đã kiểm tra mã nguồn và phát hiện ra rằng nó chỉ kiểm tra kích thước tệp để xác định sự khác biệt, không phải nội dung thực tế.
ryancdotnet

Liên quan đến -qtùy chọn Tôi không thể sao chép rằng nó chỉ kiểm tra kích thước tệp. Sử dụng GNU Diffutils 3.7 so sánh hai tệp có cùng kích thước tệp nhưng nội dung khác nhau với diff -q file1 file2đầu ra Files file1 and file2 differ.
Stefan Schmidt

50
comm -23 <(ls dir1 |sort) <(ls dir2|sort)

Lệnh này sẽ cung cấp cho bạn các tệp nằm trong dir1 chứ không phải trong dir2.

Về <( )dấu hiệu, bạn có thể google nó dưới dạng 'thay thế quá trình'.


Sẽ rất tốt nếu làm việc với các thư mục con, tôi nghĩ (ls -R dir1|sort)có thể thực hiện được mánh khóe
ulkas

1
Điều này sẽ hoạt động trên chế độ phục hồi OS X.
Anthony Vanover

@ulkas, đầu ra có thể không chính xác nếu bạn sử dụng (ls -R dir|sort).
Andriy Makukha

3
vimdiff cung cấp một so sánh trực quan đẹp hơn nhiều với màu sắc nổi bật:vimdiff <(ls dir1 |sort) <(ls dir2|sort)
Logan Reed

32

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à chuyển nó thành một tệp:

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

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

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

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

diff dir1.txt dir2.txt

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 khác để thực hiện công việc là sử dụng git

git diff --no-index dir1/ dir2/

Trân trọng!


1
Tôi không đi git có thể làm khác với các thư mục tùy ý không nằm trong git repo ... tuyệt vời !!! Câu trả lời này chỉ giải quyết một vấn đề lớn cho tôi, cảm ơn bạn
ViktorNova

17

Meld ( http://meldmerge.org/ ) thực hiện công việc tuyệt vời trong việc so sánh các thư mục và các tệp trong đó.

Meld so sánh các thư mục


Ngoại trừ meld thực hiện một công việc tệ hại khi kết thúc dòng ...
0xC0000022L

1
Không bao giờ có vấn đề với kết thúc dòng. Bạn có thể nói chi tiết?
Catalin Hritcu

Có, nó không chỉ ra kết thúc dòng. Điều này đã (lặp đi lặp lại) dẫn đến các nhà phát triển sử dụng công cụ này cam kết các thay đổi "cố định" các kết thúc dòng bằng cách tạo CRLF thành CRLFLF, ví dụ.
0xC0000022L

3
Nó cũng nhấn mạnh vào việc đọc nội dung tệp và do đó gần như vô dụng với các thư mục >> 1GB.
Tomislav Nakic-Alfirevic

13

Plugin DirDiff của vim là một công cụ rất hữu ích khác để so sánh các thư mục.

vim -c "DirDiff dir1 dir2"

Nó không chỉ liệt kê các tệp nào khác nhau giữa các thư mục mà còn cho phép bạn kiểm tra / sửa đổi với vimdiff các tệp khác nhau.


11

Không hài lòng với tất cả các phản hồi, vì hầu hết chúng hoạt động rất chậm và tạo ra đầu ra dài không cần thiết cho các thư mục lớn, tôi đã viết tập lệnh Python của riêng tôi để 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 bên trong các thư mục con bị thiếu trong thư mục khác. Vì vậy, đầu ra khá súc tích và kịch bản hoạt động nhanh.

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

Sử dụng mẫu:

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

Hoặc nếu bạn muốn chỉ xem các tệp từ thư mục đầu tiên:

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 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 cập nhật ở đây: https://gist.github.com/amakukha/f489cbde2afd32817f8e866cf4abe779


Tập lệnh đủ đơn giản thực hiện chính xác những gì tôi muốn: Xác minh bản sao hàng loạt: +1 từ tôi. (được chuyển đổi sang python2) Gợi ý: việc sử dụng các bộ có thể làm cho phần khác biệt đơn giản hơn.
Jason Morgan

6

Một cách tiếp cận khác (có thể nhanh hơn cho các thư mục lớn):

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

Các sedlệnh loại bỏ các thành phần thư mục đầu tiên nhờ Erik`s bài )


1
Tôi tin rằng phương pháp này đơn giản hơn (vẫn sử dụng finddo đó là một nhận xét và không phải là một câu trả lời riêng biệt): cd dir2; find . -exec [ -e ../dir1/{} ] \; -o -print 2>/dev/null Điều này sẽ in các tệp có trong dir2 nhưng không có trong dir1.
Alexander Amelkin

5

Điều này hơi muộn nhưng có thể giúp được ai đó. Không chắc chắn nếu diff hoặc rsync phun ra chỉ tên tệp ở định dạng trần như thế này. Cảm ơn plhn đã đưa ra giải pháp tốt đẹp mà tôi đã mở rộng dưới đây.

Nếu bạn chỉ muốn tên tệp để dễ dàng sao chép các tệp bạn cần ở định dạng rõ ràng, bạn có thể sử dụng lệnh find.

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

Điều này giả định rằng cả dir1 và dir2 đều nằm trong cùng một thư mục cha. sed chỉ cần loại bỏ thư mục cha mẹ để bạn có thể so sánh táo với táo. Sed cuối cùng chỉ đặt lại tên dir1.

Nếu bạn chỉ muốn tập tin:

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

Tương tự cho các thư mục:

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

1
Lưu ý rằng bạn có thể thực hiện cdtrước findthay vì phải sử dụng sed, ví dụ : comm -23 <(cd dir1 || exit; find -type f | sort) <(cd dir2 || exit; find -type f | sort). (Các exits ở đây để ngăn không cho findsử dụng thư mục hiện tại sẽ cdthất bại.)
phk

Cũng lưu ý rằng giải pháp của bạn có thể thất bại khi có các tệp có một số ký tự đặc biệt, nếu bạn có phiên bản commhỗ trợ rất gần đây -z(đi kèm với git.savannah.gnu.org/cgit/coreutils.git/commit/ trộm ) bạn có thể làm comm -23 -z <(cd dir1 && find -type f -print0 | sort -z) <(cd dir2 && find -type f -print0 | sort -z). (Trong lúc đó tôi cũng đã tìm ra rằng exits có thể được thay thế.)
phk

5

Câu trả lời được chấp nhận cũng sẽ liệt kê các tệp tồn tại trong cả hai thư mục, nhưng có nội dung khác nhau. Để liệt kê CHỈ các tệp tồn tại trong dir1, bạn có thể sử dụng:

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

Giải trình:

  • diff -r dir1 dir2: so sánh
  • grep 'Chỉ trong': nhận các dòng có chứa 'Chỉ trong'
  • grep dir1: lấy các dòng có chứa dir

5

Câu trả lời này tối ưu hóa một trong những gợi ý từ @ Adail-Junior bằng cách thêm -Dtùy chọn, rất hữu ích khi không có thư mục nào được so sánh là kho git:

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

Nếu bạn sử dụng -Dthì bạn sẽ không thấy so sánh với /dev/null: text Binary files a/whatever and /dev/null differ


Rất hữu ích trong việc so sánh hai thư mục, bạn thấy ngay sự khác biệt giữa các tệp. Tất nhiên là làm việc tốt nhất trên các tập tin với nội dung văn bản.
Erich Kuester

1

Một cách đơn giản để so sánh 2 thư mục bằng lệnh DIFF

tên tệp diff.1 tên tệp.2> filename.dat >> Enter

mở filename.dat sau khi chạy xong

và bạn sẽ thấy: Chỉ trong tên tệp.1: tên tệp.2 Chỉ trong: thư mục_name: name_of_file1 Chỉ có trong: thư mục_Name: name_of_file2


Tại sao bạn phải xuất ra tệp .dat?
Vishnu NK

1

Đây là tập lệnh bash để in các lệnh để đồng bộ hai thư mục

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 grepcó thể đảo ngược tìm kiếm với tùy chọn -v. Điều này làm cho grepbáo cáo các dòng, không phù hợp. Bằng cách này, bạn có thể xóa các tệp trong dir2danh sách các tệp trong dir1.

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

Các tùy chọn -F -xcho biết grepđể thực hiện tìm kiếm chuỗi trên toàn bộ dòng.

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.