ค้นหาไฟล์ที่มีอยู่ในหนึ่งไดเรกทอรี แต่ไม่อยู่ในอีก [ปิด]


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
ฉันต้องการgrepsth ^dir1เพื่อให้แน่ใจว่าฉันจะไม่dir1ปรากฏในภายหลังในเส้นทาง
Alfe

@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 pages จะพูดว่า "Output only ไม่ว่าจะเป็นไฟล์ที่แตกต่างกัน" ไม่ใช่วิธีการตรวจสอบว่ามันแตกต่างกันหรือไม่ ฉันตรวจดูซอร์สโค้ดและค้นพบว่ามันตรวจสอบขนาดไฟล์เท่านั้นเพื่อกำหนดความแตกต่างไม่ใช่เนื้อหาจริง
ryancdotnet

เกี่ยวกับ-qตัวเลือกฉันไม่สามารถทำซ้ำได้ว่าจะตรวจสอบขนาดไฟล์เท่านั้น ใช้ GNU Diffutils 3.7 เปรียบเทียบสองไฟล์ที่มีขนาดไฟล์เหมือนกัน แต่เนื้อหาที่แตกต่างกับเอาท์พุทdiff -q file1 file2 Files file1 and file2 differ
Stefan Schmidt

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

คำสั่งนี้จะให้ไฟล์ที่อยู่ใน dir1 และไม่ใช่ใน dir2

เกี่ยวกับการ<( )ลงชื่อคุณสามารถ google มันเป็น 'กระบวนการทดแทน'


มันน่าจะดีกว่าการทำงานยังมีไดเรกทอรีย่อยผมคิดว่า(ls -R dir1|sort)สามารถทำเคล็ดลับ
ulkas

1
สิ่งนี้จะทำงานในโหมดการกู้คืน OS X
Anthony Vanover

@ulkas (ls -R dir|sort)การส่งออกที่อาจจะไม่ถูกต้องถ้าคุณใช้
Andriy Makukha

3
vimdiff ให้การเปรียบเทียบภาพที่ดีกว่าพร้อมการเน้นสี:vimdiff <(ls dir1 |sort) <(ls dir2|sort)
Logan Reed

32

วิธีที่ดีในการทำการเปรียบเทียบนี้คือใช้findกับmd5suma แล้ว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 diff --no-index dir1/ dir2/

ขอแสดงความนับถืออย่างสูง!


1
ฉันไม่ได้ไปคอมไพล์สามารถทำไดเร็กตอรี่ในไดเรกทอรีต่าง ๆ ที่ไม่ได้อยู่ใน repo คอมไพล์ ... ยอดเยี่ยม !!! คำตอบนี้แก้ปัญหาใหญ่ให้ฉันขอบคุณ
ViktorNova

17

Meld ( http://meldmerge.org/ ) ทำได้ดีมากในการเปรียบเทียบไดเรกทอรีและไฟล์ภายใน

ผสานไดเรกทอรีเปรียบเทียบ


ยกเว้น meld จะทำงานที่มีหมัดเมื่อถึงจุดสิ้นสุดของบรรทัด ...
0xC0000022L

1
ไม่เคยมีปัญหากับการสิ้นสุดบรรทัด คุณช่วยรายละเอียดได้ไหม
Catalin Hritcu

ใช่มันไม่ได้บ่งบอกถึงจุดสิ้นสุดของบรรทัด สิ่งนี้มี (ซ้ำ ๆ ) ทำให้นักพัฒนาซอฟต์แวร์ที่ใช้เครื่องมือนี้ยอมรับการเปลี่ยนแปลงที่ "แก้ไข" จุดสิ้นสุดของบรรทัดโดยการทำให้ CRLF เป็น CRLFLF เป็นต้น
0xC0000022L

3
นอกจากนี้ยังยืนยันในการอ่านเนื้อหาไฟล์และจึงไม่มีประโยชน์กับไดเรกทอรี >> 1GB
Tomislav Nakic-Alfirevic

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) คำแนะนำ: การใช้ชุดอาจทำให้ส่วนต่างง่ายขึ้น
Jason Morgan

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
Alexander Amelkin

5

ช้าไปหน่อย แต่อาจช่วยใครซักคน ไม่แน่ใจว่า diff หรือ 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). (ในระหว่างนี้ฉันก็พบว่าexitสามารถแทนที่ s ได้)
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


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

1

วิธีที่ง่ายกว่าในการเปรียบเทียบ 2 ไดเรกทอรีโดยใช้คำสั่ง DIFF

diff filename.1 filename.2> filename.dat >> Enter

เปิด filename.dat หลังจากการรันเสร็จสมบูรณ์

และคุณจะเห็น: เฉพาะใน filename.1: filename.2 เฉพาะใน: directory_name: name_of_file1 เฉพาะใน: directory_Name: name_of_file2


ทำไมคุณต้องส่งออกไปยังไฟล์. dat?
พระนารายณ์ NK

1

นี่คือสคริปต์ทุบตีเพื่อพิมพ์คำสั่งสำหรับการซิงค์สองไดเรกทอรี

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การรายงานบรรทัดที่ไม่ตรงกัน โดยวิธีการนี้คุณสามารถลบไฟล์ในจากรายชื่อของไฟล์ในdir2dir1

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

ตัวเลือก-F -xบอกgrepให้ทำการค้นหาสตริงบนทั้งบรรทัด

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