การเปรียบเทียบเนื้อหาของสองไดเรกทอรี


92

ฉันมีสองไดเรกทอรีที่ควรมีไฟล์เดียวกันและมีโครงสร้างไดเรกทอรีเดียวกัน

ฉันคิดว่ามีบางอย่างขาดหายไปในไดเรกทอรีเหล่านี้

การใช้ bash shell มีวิธีเปรียบเทียบไดเรกทอรีของฉันและดูว่าหนึ่งในนั้นไม่มีไฟล์ที่มีอยู่ในอีกอันหนึ่งหรือไม่


1
อะไรคือผลลัพธ์ของbash --version?
jobin

1
ที่คล้ายกัน แต่เฉพาะเจาะจงมากขึ้น: stackoverflow.com/questions/16787916/ …
Ciro Santilli 事件改造中心中心法轮功六四事件

คำตอบ:


63

วิธีที่ดีที่จะทำการเปรียบเทียบนี้คือการใช้findด้วยแล้วmd5sumdiff

ตัวอย่าง

ใช้ find เพื่อแสดงรายการไฟล์ทั้งหมดในไดเรกทอรีจากนั้นคำนวณแฮช md5 สำหรับแต่ละไฟล์และไปป์ที่เรียงตามชื่อไฟล์เป็นไฟล์:

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

ทำโพรซีเดอร์เดียวกันกับไดเร็กทอรีอื่น:

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

จากนั้นเปรียบเทียบผลลัพธ์สองไฟล์ด้วยdiff:

diff -u dir1.txt dir2.txt

หรือเป็นคำสั่งเดียวที่ใช้การทดแทนกระบวนการ:

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

หากคุณต้องการดูเฉพาะการเปลี่ยนแปลง:

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

คำสั่ง cut พิมพ์เฉพาะแฮช (ฟิลด์แรก) ที่จะเปรียบเทียบโดย diff มิฉะนั้น diff จะพิมพ์ทุกบรรทัดในขณะที่เส้นทางไดเรกทอรีแตกต่างกันแม้ว่าแฮชจะเหมือนกัน

แต่คุณจะไม่ทราบว่าไฟล์ใดเปลี่ยนแปลง ...

เพื่อที่คุณสามารถลองสิ่งที่ชอบ

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

กลยุทธ์นี้มีประโยชน์มากเมื่อเปรียบเทียบสองไดเรกทอรีที่ไม่ได้อยู่ในเครื่องเดียวกันและคุณต้องตรวจสอบให้แน่ใจว่าไฟล์มีความเท่าเทียมกันในทั้งสองไดเรกทอรี

อีกวิธีที่ดีในการทำงานคือการใช้diffคำสั่งของ Git (อาจทำให้เกิดปัญหาเมื่อไฟล์มีการอนุญาตที่แตกต่างกัน -> ไฟล์ทุกไฟล์จะถูกแสดงในเอาท์พุทแล้ว):

git diff --no-index dir1/ dir2/

1
วิธีนี้ใช้ไม่ได้หากไม่มีขั้นตอนการเรียงพิเศษเนื่องจากลำดับที่findจะแสดงรายการไฟล์จะแตกต่างกันโดยทั่วไประหว่างสองไดเรกทอรี
Faheem Mitha

1
หนึ่งสามารถใช้วิธีการที่อธิบายไว้ในaskubuntu.com/a/662383/15729เพื่อจัดเรียงไฟล์
Faheem Mitha

1
ฉันได้รับข้อผิดพลาด `` ค้นหา: md5sum: ไม่มีไฟล์หรือไดเรกทอรีดังกล่าว
Houman

1
@Houman ฉันไม่ทราบว่าคุณกำลังใช้ Linux Distro อะไร แต่บางทีคุณต้องติดตั้งแพ็คเกจที่จะให้เดอ md5sum ใน Fedora 26 คุณสามารถติดตั้งได้ด้วย: #dnf ติดตั้ง coreutils
Adail Junior

ใช้ md5 () แทน
boj

81

คุณสามารถใช้diffคำสั่งเช่นเดียวกับที่คุณใช้สำหรับไฟล์:

diff <directory1> <directory2>

หากคุณต้องการดูโฟลเดอร์ย่อยและไฟล์ - คุณสามารถใช้-rตัวเลือก:

diff -r <directory1> <directory2>

2
ไม่ทราบว่าใช้diffงานได้กับไดเรกทอรีเช่นกัน (คนต่างยืนยันว่า) แต่วิธีนี้จะไม่ตรวจสอบการเปลี่ยนแปลงในไดเรกทอรีย่อยซ้ำ ๆ ภายในไดเรกทอรีย่อย
jobin

1
@Jobin แปลก ... สำหรับฉันมันใช้งานได้
Alex R.

1
a/b/c/d/aฉันมีบางอย่างเช่นนี้ x/b/c/d/b, ดูว่าอะไรdiff a xให้คุณ
jobin

2
คุณต้องใช้-rตัวเลือก นั่น ( diff -r a x) ให้ฉัน:Only in a/b/c/d: a. only in x/b/c/d: b.
อเล็กซ์อาร์

3
diff แสดงให้ฉันเห็นความแตกต่างของไฟล์ INTO แต่ไม่ใช่ถ้าไดเรกทอรีมีไฟล์ที่อีกอันหนึ่งไม่มี !!! ฉันไม่ต้องการทราบความแตกต่างในไฟล์ แต่ยังถ้าไฟล์มีอยู่ในไดเรกทอรีและไม่ได้อยู่ในอีกคนหนึ่ง
AndreaNobili

25

เมื่อคุณไม่ใช้ bash คุณสามารถทำได้โดยใช้ diff ด้วย--briefและ--recursive:

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

man diffมีตัวเลือกทั้งสอง:

-q, --brief
รายงานเฉพาะเมื่อไฟล์ที่แตกต่างกัน

-r, --recursive
ซ้ำเปรียบเทียบไดเรกทอรีย่อยใด ๆ ที่พบ


13

นี่เป็นทางเลือกเพื่อเปรียบเทียบเพียงชื่อไฟล์และไม่ใช่เนื้อหา:

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

นี่เป็นวิธีที่ง่ายในการแสดงรายการไฟล์ที่หายไป แต่แน่นอนว่ามันจะไม่ตรวจจับไฟล์ที่มีชื่อเดียวกัน แต่มีเนื้อหาต่างกัน!

(ส่วนตัวฉันใช้diffdirsสคริปต์ของตัวเองแต่นั่นเป็นส่วนหนึ่งของห้องสมุดที่ใหญ่กว่า )


3
คุณควรที่จะเปลี่ยนตัวกระบวนการใช้ดีกว่าไม่ไฟล์ temp ...
mniip

3
โปรดทราบว่านี่ไม่สนับสนุนชื่อไฟล์ที่มีอักขระพิเศษบางตัวในกรณีนี้คุณอาจต้องการใช้ตัวคั่นศูนย์ซึ่ง AFAIK diffไม่สนับสนุนในตอนนี้ แต่มีสิ่งcommที่สนับสนุนมันตั้งแต่git.savannah.gnu.org/cgit/coreutils.git/commit/ ......ดังนั้นเมื่อมันมาถึง coreutils ที่อยู่ใกล้คุณคุณสามารถทำได้comm -z <(cd folder1 && find -print0 | sort) <(cd folder2 && find -print0 | sort -z)(ซึ่งผลลัพธ์ที่คุณอาจต้องแปลงเพิ่มเติมในรูปแบบ คุณต้องใช้--output-delimiterพารามิเตอร์และเครื่องมือเพิ่มเติม)
phk

7

บางทีตัวเลือกหนึ่งคือเรียกใช้ rsync สองครั้ง:

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

ด้วยบรรทัดก่อนหน้าคุณจะได้รับไฟล์ที่อยู่ใน dir1 และแตกต่างกัน (หรือหายไป) ใน dir2

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

เช่นเดียวกันสำหรับ 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

คุณสามารถลบ-nตัวเลือกเพื่อรับการเปลี่ยนแปลง นั่นคือการคัดลอกรายการไฟล์ไปยังโฟลเดอร์ที่สอง

ในกรณีที่คุณทำเช่นนั้นอาจใช้ตัวเลือกที่ดี-uเพื่อหลีกเลี่ยงการเขียนทับไฟล์ใหม่

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

หนึ่งซับ:

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

3

หากคุณต้องการทำให้แต่ละไฟล์สามารถขยายและยุบได้คุณสามารถdiff -rไพพ์เอาต์พุตของเป็น Vim

ก่อนอื่นเราขอมอบกฎการพับเป็นกลุ่ม:

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

ตอนนี้เพียง:

diff -r dir1 dir2 | vim -

คุณสามารถกดzoและzcเปิดและปิดการพับ หากต้องการออกจากกลุ่มให้กด:q<Enter>


3

ค่อนข้างง่ายสำหรับงานที่จะทำให้สำเร็จในงูหลาม:

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

ค่าที่แท้จริงแทนสำหรับการและDIR1 DIR2

นี่คือตัวอย่างการรัน:

$ 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

เพื่อความสะดวกในการอ่านนี่เป็นสคริปต์จริงแทนที่จะเป็นสายการบินเดียว:

#!/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
โปรดทราบว่าการos.listdirไม่ได้ให้คำสั่งเฉพาะใด ๆ ดังนั้นรายการอาจมีสิ่งเดียวกันในลำดับที่แตกต่างกันและการเปรียบเทียบจะล้มเหลว
muru

1
@muru จุดที่ดีฉันจะรวมถึงการเรียงลำดับที่
Sergiy Kolodyazhnyy

3

แรงบันดาลใจจากคำตอบของ Sergiy ฉันเขียนสคริปต์ 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])

หากคุณบันทึกลงในไฟล์ชื่อcompare_dirs.pyคุณสามารถเรียกใช้งานด้วย Python3.x:

python3 compare_dirs.py dir1 dir2

ตัวอย่างผลลัพธ์:

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 หากคุณต้องการเปรียบเทียบขนาดไฟล์และแฮชไฟล์สำหรับการเปลี่ยนแปลงที่อาจเกิดขึ้นฉันเผยแพร่สคริปต์ที่อัปเดตที่นี่: https://gist.github.com/amakukha/f489cbde2afd32817f8e866cf4abe779


1
ขอขอบคุณฉันเพิ่มตัวเลือก param ที่สามที่เป็นตัวเลือกเพื่อข้าม / ไม่สนใจ gist.github.com/mscalora/e86e2bbfd3c24a7c1784f3d692b1c684เพื่อทำสิ่งที่ฉันต้องการ:cmpdirs dir1 dir2 '/\.git/'
Mike


0

ผมอยากจะขอแนะนำเครื่องมือที่ดีที่ฉันมีเพียงค้นพบ: MELD

มันทำงานได้อย่างถูกต้องและทุกสิ่งที่คุณสามารถทำได้ด้วยคำสั่งdiffบนระบบที่ใช้ Linux สามารถทำซ้ำได้ด้วยส่วนต่อประสานกราฟิกที่ดี! สนุก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.