ฉันมีสองไดเรกทอรีที่ควรมีไฟล์เดียวกันและมีโครงสร้างไดเรกทอรีเดียวกัน
ฉันคิดว่ามีบางอย่างขาดหายไปในไดเรกทอรีเหล่านี้
การใช้ bash shell มีวิธีเปรียบเทียบไดเรกทอรีของฉันและดูว่าหนึ่งในนั้นไม่มีไฟล์ที่มีอยู่ในอีกอันหนึ่งหรือไม่
ฉันมีสองไดเรกทอรีที่ควรมีไฟล์เดียวกันและมีโครงสร้างไดเรกทอรีเดียวกัน
ฉันคิดว่ามีบางอย่างขาดหายไปในไดเรกทอรีเหล่านี้
การใช้ bash shell มีวิธีเปรียบเทียบไดเรกทอรีของฉันและดูว่าหนึ่งในนั้นไม่มีไฟล์ที่มีอยู่ในอีกอันหนึ่งหรือไม่
คำตอบ:
วิธีที่ดีที่จะทำการเปรียบเทียบนี้คือการใช้find
ด้วยแล้วmd5sum
diff
ใช้ 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/
find
จะแสดงรายการไฟล์จะแตกต่างกันโดยทั่วไประหว่างสองไดเรกทอรี
คุณสามารถใช้diff
คำสั่งเช่นเดียวกับที่คุณใช้สำหรับไฟล์:
diff <directory1> <directory2>
หากคุณต้องการดูโฟลเดอร์ย่อยและไฟล์ - คุณสามารถใช้-r
ตัวเลือก:
diff -r <directory1> <directory2>
diff
งานได้กับไดเรกทอรีเช่นกัน (คนต่างยืนยันว่า) แต่วิธีนี้จะไม่ตรวจสอบการเปลี่ยนแปลงในไดเรกทอรีย่อยซ้ำ ๆ ภายในไดเรกทอรีย่อย
a/b/c/d/a
ฉันมีบางอย่างเช่นนี้ x/b/c/d/b
, ดูว่าอะไรdiff a x
ให้คุณ
-r
ตัวเลือก นั่น ( diff -r a x
) ให้ฉัน:Only in a/b/c/d: a. only in x/b/c/d: b.
เมื่อคุณไม่ใช้ bash คุณสามารถทำได้โดยใช้ diff ด้วย--brief
และ--recursive
:
$ diff -rq dir1 dir2
Only in dir2: file2
Only in dir1: file1
man diff
มีตัวเลือกทั้งสอง:
-q
,--brief
รายงานเฉพาะเมื่อไฟล์ที่แตกต่างกัน
-r
,--recursive
ซ้ำเปรียบเทียบไดเรกทอรีย่อยใด ๆ ที่พบ
นี่เป็นทางเลือกเพื่อเปรียบเทียบเพียงชื่อไฟล์และไม่ใช่เนื้อหา:
diff <(cd folder1 && find . | sort) <(cd folder2 && find . | sort)
นี่เป็นวิธีที่ง่ายในการแสดงรายการไฟล์ที่หายไป แต่แน่นอนว่ามันจะไม่ตรวจจับไฟล์ที่มีชื่อเดียวกัน แต่มีเนื้อหาต่างกัน!
(ส่วนตัวฉันใช้diffdirs
สคริปต์ของตัวเองแต่นั่นเป็นส่วนหนึ่งของห้องสมุดที่ใหญ่กว่า )
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
พารามิเตอร์และเครื่องมือเพิ่มเติม)
บางทีตัวเลือกหนึ่งคือเรียกใช้ 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/
หากคุณต้องการทำให้แต่ละไฟล์สามารถขยายและยุบได้คุณสามารถ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>
ค่อนข้างง่ายสำหรับงานที่จะทำให้สำเร็จในงูหลาม:
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")
os.listdir
ไม่ได้ให้คำสั่งเฉพาะใด ๆ ดังนั้นรายการอาจมีสิ่งเดียวกันในลำดับที่แตกต่างกันและการเปรียบเทียบจะล้มเหลว
แรงบันดาลใจจากคำตอบของ 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
cmpdirs dir1 dir2 '/\.git/'
ฉันจะเพิ่มในรายการนี้เป็นทางเลือกของ NodeJs ที่ฉันได้เขียนบางเวลาที่ผ่านมา
npm install dir-compare -g
dircompare dir1 dir2
bash --version
?