Cách nhanh nhất để biết hai tệp có cùng nội dung trong Unix / Linux không?


231

Tôi có một tập lệnh shell trong đó tôi cần kiểm tra xem hai tệp có chứa cùng một dữ liệu hay không. Tôi làm điều này cho rất nhiều tệp và trong kịch bản của tôi, difflệnh dường như là nút cổ chai hiệu năng.

Đây là dòng:

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

if ($status) then ...

Có thể có một cách nhanh hơn để so sánh các tập tin, có thể là một thuật toán tùy chỉnh thay vì mặc định diff?


10
Điều này thực sự gây nghiện, nhưng bạn không hỏi xem hai tệp có giống nhau không, bạn đang hỏi xem hai tệp có nội dung giống nhau không. Các tệp giống nhau có các nút giống hệt nhau (và cùng một thiết bị).
Zano

1
Không giống như câu trả lời được chấp nhận, phép đo trong câu trả lời này không nhận ra bất kỳ sự khác biệt đáng chú ý nào giữa diffcmp.
wedi

Câu trả lời:


390

Tôi tin rằng cmpsẽ dừng ở mức chênh lệch byte đầu tiên:

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

1
Làm thế nào tôi có thể thêm nhiều lệnh hơn chỉ một? Tôi muốn sao chép một tập tin và roboot.
feedc0de

9
cmp -s $old $newcũng hoạt động. -slà viết tắt của--silent
Rohmer

7
Khi tăng tốc độ, bạn nên kiểm tra kích thước tệp bằng nhau trước khi so sánh nội dung. Có ai biết nếu cmp làm điều này?
BeowulfNode42

3
Để chạy nhiều lệnh, bạn có thể sử dụng dấu ngoặc: cmp -s cũ mới | | {tiếng vang không; tiếng vang; tiếng vang tương tự; }
unfa

6
@ BeowulfNode42 có, bất kỳ triển khai tốt nào cmpcũng sẽ kiểm tra kích thước tệp trước tiên. Đây là phiên bản GNU, nếu bạn muốn xem các tối ưu hóa bổ sung, nó bao gồm: git.savannah.gnu.org/cgit/diffutils.git/tree/src/cmp.c
Ryan Graham

54

Tôi thích @Alex Howansky đã sử dụng 'cmp --silent' cho việc này. Nhưng tôi cần cả phản ứng tích cực và tiêu cực nên tôi sử dụng:

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

Sau đó tôi có thể chạy cái này trong terminal hoặc với ssh để kiểm tra các tệp dựa trên một tệp không đổi.


16
Nếu echo successlệnh của bạn (hoặc bất kỳ lệnh nào khác bạn đặt vào vị trí của nó) không thành công, lệnh "phản hồi tiêu cực" của bạn sẽ được chạy. Bạn nên sử dụng cấu trúc "if-then-other-fi". Ví dụ, như ví dụ đơn giản này .
tự đại diện

18

Tại sao bạn không nhận được hàm băm của cả hai tệp?

Hãy thử tập lệnh này, gọi nó là script.sh và sau đó chạy nó như sau: 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 Đó là vì thuật toán băm không phải là một. Chúng được thiết kế sao cho không gian băm lớn và các đầu vào khác nhau có cơ hội cao tạo ra các giá trị băm khác nhau. Mặc dù thực tế là không gian băm là hữu hạn, trong khi phạm vi của các tệp có thể để băm là không - cuối cùng bạn sẽ có một xung đột. Trong mật mã học, nó được gọi là Cuộc tấn công sinh nhật .
sẽ

5
@ Will Eh, nó có hiệu quả đảm bảo để làm việc. Các tỷ lệ cược của nó không hoạt động, nói một cách toán học, xung quanh 1/(2^511). Trừ khi bạn lo lắng về việc ai đó cố tình tạo ra sự va chạm , ý tưởng về phương pháp này tạo ra dương tính giả không thực sự là một mối quan tâm nghiêm trọng. cmpMặc dù vậy vẫn hiệu quả hơn, vì nó không phải đọc toàn bộ tệp trong trường hợp các tệp không khớp.
Ajedi32

12
OP đã yêu cầu cách NHANH CHÓNG nhất ... sẽ không tìm kiếm bit không khớp đầu tiên (sử dụng cmp) nhanh hơn (nếu chúng không khớp) hơn là băm toàn bộ tệp, đặc biệt là nếu các tệp lớn?
KoZm0kNoT

3
md5 là tốt nhất nếu bạn đang làm một so sánh nhiều. Bạn có thể lưu trữ băm md5 dưới dạng một thuộc tính hoặc trong cơ sở dữ liệu đối với mỗi tệp. Nếu một tệp mới xuất hiện và bạn phải kiểm tra xem cùng một tệp có tồn tại ở bất kỳ đâu trên hệ thống tệp hay không thì tất cả những gì bạn làm là tính toán hàm băm của tệp mới và kiểm tra lại tất cả trước đó. Tôi chắc chắn Git sử dụng băm để kiểm tra các thay đổi tệp trong một lần xác nhận nhưng họ sử dụng SHA1.
JimHough

3
@ BeowulfNode42 Đó là lý do tại sao tôi mở đầu nhận xét của mình bằng "Trừ khi bạn lo lắng về việc ai đó cố tình tạo ra một vụ va chạm"
Ajedi32

5

Bởi vì tôi hút và không có đủ điểm danh tiếng, tôi không thể thêm mẩu tin này vào như một bình luận.

Nhưng, nếu bạn định sử dụng cmplệnh (và không cần / muốn dài dòng), bạn có thể lấy trạng thái thoát. Trên cmptrang người đàn ông:

Nếu TẬP_TIN là '-' hoặc bị thiếu, hãy đọc đầu vào tiêu chuẩn. Trạng thái thoát là 0 nếu đầu vào giống nhau, 1 nếu khác, 2 nếu gặp sự cố.

Vì vậy, bạn có thể làm một cái gì đó như:

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

đúng, nhưng đây thực sự là cách làm phức tạp hơn cmp --silent $FILE1 $FILE2 ; if [ "$?" == "1" ]; then echo "files differ"; fi, đến lượt nó lại là cách làm phức tạp hơn cmp --silent $FILE1 $FILE2 || echo "files differ"vì bạn có thể sử dụng lệnh trong biểu thức trực tiếp. Nó thay thế cho $?. Kết quả là trạng thái tồn tại của lệnh sẽ được so sánh. Và đó là những gì câu trả lời khác làm. btw. Nếu ai đó đang vật lộn với --silent, nó không được hỗ trợ ở mọi nơi (busybox). sử dụng-s
papo

4

Đối với các tệp không khác nhau, bất kỳ phương pháp nào cũng sẽ yêu cầu phải đọc cả hai tệp, ngay cả khi quá khứ đã đọc.

Không có cách thay thế. Vì vậy, việc tạo băm hoặc tổng kiểm tra tại một số thời điểm yêu cầu đọc toàn bộ tệp. Các tập tin lớn mất thời gian.

Truy xuất siêu dữ liệu tệp nhanh hơn nhiều so với đọc một tệp lớn.

Vì vậy, có siêu dữ liệu tệp nào bạn có thể sử dụng để thiết lập rằng các tệp khác nhau không? Kích thước tập tin ? hoặc thậm chí kết quả của lệnh tập tin chỉ đọc một phần nhỏ của tập tin?

Đoạn mã ví dụ kích thước tệp:

  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'  

Nếu các tệp có cùng kích thước thì bạn bị kẹt với toàn bộ tệp đọc.


1
Sử dụng ls -nđể tránh các vấn đề nếu tên người dùng hoặc nhóm có khoảng trắng.
tricasse

2

Cũng thử sử dụng lệnh 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

Lệnh cksum sẽ xuất ra số byte của một tệp. Xem 'người đàn ông cksum'.


2
Đó là suy nghĩ đầu tiên của tôi quá. Tuy nhiên, băm có ý nghĩa nếu bạn phải so sánh cùng một tệp nhiều lần, vì hàm băm chỉ được tính một lần. Nếu bạn chỉ so sánh nó một lần, sau đó md5đọc toàn bộ tệp, vì vậy cmp, dừng lại ở sự khác biệt đầu tiên, sẽ nhanh hơn nhiều.
Francesco Dondi

0

Thực hiện một số thử nghiệm với Raspberry Pi 3B + (Tôi đang sử dụng hệ thống tệp lớp phủ và cần đồng bộ hóa định kỳ), tôi đã chạy một so sánh của riêng tôi cho diff -q và cmp -s; lưu ý rằng đây là nhật ký từ bên trong / dev / shm, vì vậy tốc độ truy cập đĩa không phải là vấn đề:

[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

Tôi đã chạy nó một vài lần. cmp -s luôn có thời gian ngắn hơn một chút trên hộp kiểm tra tôi đang sử dụng. Vì vậy, nếu bạn muốn sử dụng cmp -s để thực hiện mọi thứ giữa hai tệp ....

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