วิธีที่เร็วที่สุดที่จะบอกได้ว่าสองไฟล์มีเนื้อหาเหมือนกันใน Unix / Linux หรือไม่?


231

ฉันมีเชลล์สคริปต์ที่ฉันต้องตรวจสอบว่าไฟล์สองไฟล์มีข้อมูลเดียวกันหรือไม่ ฉันทำสิ่งนี้เพื่อไฟล์จำนวนมากและในสคริปต์ของฉันdiffดูเหมือนว่าคำสั่งจะเป็นคอขวดของประสิทธิภาพ

นี่คือบรรทัด:

diff -q $dst $new > /dev/null

if ($status) then ...

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


10
นี่เป็นการวางยาจริง ๆ แต่คุณไม่ได้ขอให้ดูว่าสองไฟล์เหมือนกันหรือไม่คุณกำลังถามว่าสองไฟล์มีเนื้อหาเหมือนกันหรือไม่ ไฟล์เดียวกันมี inodes เหมือนกัน (และอุปกรณ์เดียวกัน)
Zano

1
ซึ่งแตกต่างจากคำตอบที่ได้รับการยอมรับการวัดในคำตอบนี้ไม่ยอมรับความแตกต่างที่โดดเด่นใด ๆ ระหว่างและdiff cmp
WEDI

คำตอบ:


388

ฉันเชื่อว่าcmpจะหยุดที่แตกต่างไบต์แรก:

cmp --silent $old $new || echo "files are different"

1
ฉันจะเพิ่มคำสั่งมากกว่าคำสั่งเดียวได้อย่างไร ฉันต้องการคัดลอกไฟล์และปล้น
feedc0de

9
cmp -s $old $newยังใช้งานได้ -sย่อมาจาก--silent
Rohmer

7
เพื่อเพิ่มความเร็วคุณควรตรวจสอบว่าขนาดไฟล์เท่ากันก่อนทำการเปรียบเทียบเนื้อหา ไม่มีใครรู้ว่า cmp ทำสิ่งนี้หรือไม่?
BeowulfNode42

3
หากต้องการเรียกใช้หลายคำสั่งคุณสามารถใช้วงเล็บ: cmp -s old new || {ไม่ก้อง; สะท้อน; สะท้อนเหมือนกัน; }
unfa

6
@ BeowulfNode42 ใช่การใช้งานที่เหมาะสมใด ๆ ของcmpจะตรวจสอบขนาดไฟล์ก่อน นี่คือรุ่น GNU หากคุณต้องการดูการเพิ่มประสิทธิภาพเพิ่มเติมประกอบด้วย: git.savannah.gnu.org/cgit/diffutils.git/tree/src/cmp.c
Ryan Graham

53

ฉันชอบ @Alex Howansky ใช้ 'cmp --silent' สำหรับสิ่งนี้แล้ว แต่ฉันต้องการการตอบสนองทั้งบวกและลบดังนั้นฉันจึงใช้:

cmp --silent file1 file2 && echo '### SUCCESS: Files Are Identical! ###' || echo '### WARNING: Files Are Different! ###'

ฉันสามารถเรียกใช้สิ่งนี้ใน terminal หรือด้วย ssh เพื่อตรวจสอบไฟล์กับไฟล์คงที่


16
หากecho successคำสั่งของคุณ(หรือคำสั่งอื่นที่คุณวางไว้) ล้มเหลวคำสั่ง "การตอบกลับเชิงลบ" ของคุณจะถูกเรียกใช้ คุณควรใช้โครงสร้าง "if-then-else-fi" ตัวอย่างเช่นตัวอย่างง่ายๆนี้
สัญลักษณ์แทน

18

ทำไมคุณไม่ได้รับแฮชของเนื้อหาไฟล์ทั้งสอง

ลองใช้สคริปต์นี้เรียกใช้ตัวอย่างเช่น script.sh แล้วเรียกใช้ดังนี้: script.sh file1.txt file2.txt

#!/bin/bash

file1=`md5 $1`
file2=`md5 $2`

if [ "$file1" = "$file2" ]
then
    echo "Files have the same content"
else
    echo "Files have NOT the same content"
fi

2
@THISUSERNEEDSHELP เป็นเพราะอัลกอริทึมการแฮชไม่ได้เป็นแบบหนึ่งต่อหนึ่ง พวกเขาได้รับการออกแบบเช่นนั้นพื้นที่ hashing มีขนาดใหญ่และปัจจัยการผลิตที่แตกต่างกันมีโอกาสสูงในการผลิตแฮชที่แตกต่างกัน แม้ว่าในความเป็นจริงนั้นพื้นที่แฮชจะมีขอบเขต จำกัด ในขณะที่ช่วงของไฟล์ที่เป็นไปได้สำหรับแฮชไม่ได้เป็นไปได้ในที่สุดคุณก็จะได้รับการชนกัน ใน cryptology มันเรียกว่าการโจมตีวันเกิด
จะ

5
@ จะเอ๊ะรับประกันการทำงานได้อย่างมีประสิทธิภาพ 1/(2^511)อัตราต่อรองของมันไม่ได้ทำงานเป็นทางคณิตศาสตร์พูดรอบ ถ้าคุณไม่กังวลเกี่ยวกับใครบางคนที่พยายามสร้างการชนกันความคิดของวิธีนี้ที่สร้างผลบวกที่ผิดพลาดนั้นไม่ได้เป็นปัญหาที่ร้ายแรง cmpยังคงมีประสิทธิภาพมากกว่าเนื่องจากไม่ต้องอ่านไฟล์ทั้งหมดในกรณีที่ไฟล์ไม่ตรงกัน
Ajedi32

12
OP ถามหาวิธีที่เร็วที่สุด ... จะไม่ค้นหาบิตที่ไม่จับคู่แรก (ใช้ cmp) เร็วกว่า (หากไม่ตรงกัน) กว่าการแฮชไฟล์ทั้งไฟล์โดยเฉพาะถ้าไฟล์มีขนาดใหญ่?
KoZm0kNoT

3
md5 ดีที่สุดถ้าคุณทำการเปรียบเทียบแบบหนึ่งต่อหลาย คุณสามารถเก็บแฮช md5 เป็นแอตทริบิวต์หรือในฐานข้อมูลกับแต่ละไฟล์ หากไฟล์ใหม่ปรากฏขึ้นและคุณต้องตรวจสอบว่ามีไฟล์เดียวกันอยู่ทุกที่ในระบบไฟล์หรือไม่จากนั้นทั้งหมดที่คุณทำคือการคำนวณแฮชของไฟล์ใหม่และตรวจสอบกับไฟล์ก่อนหน้าทั้งหมด ฉันแน่ใจว่า Git ใช้การแปลงแป้นพิมพ์เพื่อตรวจสอบการเปลี่ยนแปลงไฟล์ระหว่างการส่งมอบ แต่ใช้ SHA1
JimHough

3
@ BeowulfNode42 ซึ่งเป็นสาเหตุที่ฉันแสดงความคิดเห็นล่วงหน้าของฉันด้วย "เว้นแต่คุณจะกังวลเกี่ยวกับใครบางคนพยายามที่จะสร้างการชน"
Ajedi32

5

เนื่องจากฉันดูดและมีคะแนนชื่อเสียงไม่เพียงพอฉันจึงไม่สามารถเพิ่มชิ้นอาหารอันโอชะนี้เป็นความคิดเห็นได้

แต่ถ้าคุณจะใช้cmpคำสั่ง (และไม่ต้องการ / ต้องการให้ละเอียด) คุณก็สามารถคว้าสถานะทางออก ตามcmpหน้าคน:

ถ้าไฟล์ '-' หรือหายไปอ่านอินพุตมาตรฐาน สถานะการออกคือ 0 ถ้าอินพุตเหมือนกัน 1 ถ้าต่างกัน 2 ถ้ามีปัญหา

ดังนั้นคุณสามารถทำสิ่งที่ชอบ:

STATUS="$(cmp --silent $FILE1 $FILE2; echo $?)"  # "$?" gives exit status for each comparison

if [[$STATUS -ne 0]]; then  # if status isn't equal to 0, then execute code
    DO A COMMAND ON $FILE1
else
    DO SOMETHING ELSE
fi

ใช่ แต่นี่เป็นวิธีที่ซับซ้อนกว่าในการทำcmp --silent $FILE1 $FILE2 ; if [ "$?" == "1" ]; then echo "files differ"; fiซึ่งจะเป็นวิธีที่ซับซ้อนกว่าในการทำcmp --silent $FILE1 $FILE2 || echo "files differ"เพราะคุณสามารถใช้คำสั่งในการแสดงออกโดยตรง $?มันทดแทน ในฐานะสถานะของคำสั่งผลลัพธ์จะถูกเปรียบเทียบ และนั่นคือสิ่งที่คำตอบอื่น ๆ ทำ BTW หากใครบางคนกำลังดิ้นรนกับ--silentมันก็ไม่ได้รับการสนับสนุนทุกที่ (busybox) ใช้-s
papo

4

สำหรับไฟล์ที่ไม่แตกต่างกันวิธีการใด ๆ จะต้องมีการอ่านทั้งไฟล์โดยสิ้นเชิงแม้ว่าการอ่านจะเคยผ่านมาแล้ว

ไม่มีทางเลือกอื่น ดังนั้นการสร้างแฮชหรือเช็คซัมในบางช่วงเวลาจำเป็นต้องอ่านไฟล์ทั้งหมด ไฟล์ขนาดใหญ่ต้องใช้เวลา

การดึงข้อมูลเมตาของไฟล์เร็วกว่าการอ่านไฟล์ขนาดใหญ่

ดังนั้นมีเมทาดาทาไฟล์ใดบ้างที่คุณสามารถใช้เพื่อสร้างว่าไฟล์ต่างกันหรือไม่? ขนาดไฟล์? หรือแม้กระทั่งผลลัพธ์ของคำสั่ง file ที่เพิ่งอ่านส่วนเล็ก ๆ ของไฟล์?

ตัวอย่างรหัสขนาดไฟล์:

  ls -l $1 $2 | 
  awk 'NR==1{a=$5} NR==2{b=$5} 
       END{val=(a==b)?0 :1; exit( val) }'

[ $? -eq 0 ] && echo 'same' || echo 'different'  

หากไฟล์มีขนาดเท่ากันแสดงว่าคุณติดอยู่กับการอ่านไฟล์แบบเต็ม


1
ใช้ls -nเพื่อหลีกเลี่ยงปัญหาหากชื่อผู้ใช้หรือกลุ่มมีช่องว่าง
tricasse

2

ลองใช้คำสั่ง cksum ด้วย:

chk1=`cksum <file1> | awk -F" " '{print $1}'`
chk2=`cksum <file2> | awk -F" " '{print $1}'`

if [ $chk1 -eq $chk2 ]
then
  echo "File is identical"
else
  echo "File is not identical"
fi

คำสั่ง cksum จะส่งออกจำนวนไบต์ของไฟล์ ดู 'man cksum'


2
นั่นเป็นความคิดแรกของฉันด้วย อย่างไรก็ตามแฮชเหมาะสมถ้าคุณต้องเปรียบเทียบไฟล์เดียวกันหลายครั้งเนื่องจากแฮชคำนวณเพียงครั้งเดียว หากคุณกำลังเปรียบเทียบเพียงครั้งเดียวให้md5อ่านไฟล์ทั้งหมดต่อไปดังนั้นcmpการหยุดที่ข้อแตกต่างแรกจะเร็วขึ้น
Francesco Dondi

0

ทำการทดสอบบางอย่างกับ Raspberry Pi 3B + (ฉันใช้ระบบไฟล์ซ้อนทับและจำเป็นต้องซิงค์เป็นระยะ) ฉันทำการเปรียบเทียบตัวเองสำหรับ diff -q และ cmp -s; โปรดทราบว่านี่เป็นบันทึกจากภายใน / dev / shm ดังนั้นความเร็วในการเข้าถึงดิสก์จึงไม่ใช่ปัญหา:

[root@mypi shm]# dd if=/dev/urandom of=test.file bs=1M count=100 ; time diff -q test.file test.copy && echo diff true || echo diff false ; time cmp -s test.file test.copy && echo cmp true || echo cmp false ; cp -a test.file test.copy ; time diff -q test.file test.copy && echo diff true || echo diff false; time cmp -s test.file test.copy && echo cmp true || echo cmp false
100+0 records in
100+0 records out
104857600 bytes (105 MB) copied, 6.2564 s, 16.8 MB/s
Files test.file and test.copy differ

real    0m0.008s
user    0m0.008s
sys     0m0.000s
diff false

real    0m0.009s
user    0m0.007s
sys     0m0.001s
cmp false
cp: overwrite âtest.copyâ? y

real    0m0.966s
user    0m0.447s
sys     0m0.518s
diff true

real    0m0.785s
user    0m0.211s
sys     0m0.573s
cmp true
[root@mypi shm]# pico /root/rwbscripts/utils/squish.sh

ฉันวิ่งไปสองสามครั้ง cmp -s มีเวลาสั้นลงเล็กน้อยในกล่องทดสอบที่ฉันใช้ ดังนั้นหากคุณต้องการใช้ cmp -s เพื่อทำสิ่งต่าง ๆ ระหว่างสองไฟล์ ....

identical (){
  echo "$1" and "$2" are the same.
  echo This is a function, you can put whatever you want in here.
}
different () {
  echo "$1" and "$2" are different.
  echo This is a function, you can put whatever you want in here, too.
}
cmp -s "$FILEA" "$FILEB" && identical "$FILEA" "$FILEB" || different "$FILEA" "$FILEB"
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.