Cara tercepat untuk mengetahui apakah dua file memiliki konten yang sama di Unix / Linux?

231

Saya memiliki skrip shell di mana saya perlu memeriksa apakah dua file berisi data yang sama atau tidak. Saya melakukan ini untuk banyak file, dan dalam skrip saya diffperintah ini tampaknya menjadi hambatan kinerja.

Inilah intinya:

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

if ($status) then ...

Mungkinkah ada cara yang lebih cepat untuk membandingkan file, mungkin algoritma khusus alih-alih yang default diff?

JDS
sumber
10
Ini benar-benar membingungkan, tetapi Anda tidak meminta untuk melihat apakah dua file sama, Anda bertanya apakah dua file memiliki konten yang identik. File yang sama memiliki inode yang identik (dan perangkat yang sama).
Zano
1
Tidak seperti jawaban yang diterima, pengukuran dalam jawaban ini tidak mengenali perbedaan penting antara diffdan cmp.
Dipakai

Jawaban:

390

Saya percaya cmpakan berhenti pada perbedaan byte pertama:

cmp --silent $old $new || echo "files are different"
Alex Howansky
sumber
1
Bagaimana saya bisa menambahkan lebih banyak perintah daripada hanya satu? Saya ingin menyalin file dan roboot.
feedc0de
9
cmp -s $old $newjuga berfungsi. -skependekan dari--silent
Rohmer
7
Sebagai penambah kecepatan, Anda harus memeriksa ukuran file yang sama sebelum membandingkan konten. Apakah ada yang tahu jika CMP melakukan ini?
BeowulfNode42
3
Untuk menjalankan banyak perintah, Anda dapat menggunakan tanda kurung: cmp -s new || {echo not; gema; gema sama; }
unfa
6
@ BeowulfNode42 ya, implementasi yang layak cmpakan memeriksa ukuran file terlebih dahulu. Ini versi GNU, jika Anda ingin melihat optimasi tambahan yang meliputi: git.savannah.gnu.org/cgit/diffutils.git/tree/src/cmp.c
Ryan Graham
54

Saya suka @Alex Howansky telah menggunakan 'cmp --silent' untuk ini. Tetapi saya membutuhkan respons positif dan negatif sehingga saya menggunakan:

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

Saya kemudian dapat menjalankan ini di terminal atau dengan ssh untuk memeriksa file terhadap file yang konstan.

pn1 dude
sumber
16
Jika echo successperintah Anda (atau perintah apa pun yang Anda masukkan) gagal, perintah "tanggapan negatif" Anda akan dijalankan. Anda harus menggunakan konstruksi "if-then-else-fi". Misalnya, suka contoh sederhana ini .
Wildcard
18

Mengapa Anda tidak mendapatkan hash dari kedua konten file?

Coba skrip ini, panggil misalnya script.sh dan kemudian jalankan sebagai berikut: 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
jabaldonedo
sumber
2
@ THISUSERNEEDSHELP Itu karena algoritma hashing tidak satu ke satu. Mereka dirancang sedemikian rupa sehingga ruang hashing besar, dan input yang berbeda memiliki peluang tinggi untuk menghasilkan hash yang berbeda. Kenyataannya adalah bahwa ruang hash terbatas, sedangkan rentang file yang mungkin untuk hash tidak - pada akhirnya Anda akan mengalami tabrakan. Dalam kriptologi itu disebut Serangan Ulang Tahun .
akan
5
@will Eh, dijamin efektif untuk bekerja. Kemungkinannya tidak berfungsi adalah, secara matematis, ada 1/(2^511). Kecuali jika Anda khawatir tentang seseorang yang sengaja mencoba membuat tabrakan , ide metode ini menghasilkan false positive bukanlah masalah serius. cmpmasih lebih efisien, karena tidak harus membaca seluruh file dalam kasus di mana file tidak cocok.
Ajedi32
12
OP meminta cara TERCEPAT ... tidak akan mencari bit yang tidak cocok pertama (menggunakan cmp) lebih cepat (jika mereka tidak cocok) daripada hashing seluruh file, terutama jika file besar?
KoZm0kNoT
3
md5 adalah yang terbaik jika Anda melakukan perbandingan satu ke banyak. Anda dapat menyimpan hash md5 sebagai atribut atau dalam database terhadap setiap file. Jika file baru muncul dan Anda harus memeriksa apakah file yang sama ada di mana saja pada sistem file maka yang Anda lakukan hanyalah menghitung hash dari file baru dan memeriksa semua sebelumnya. Saya yakin Git menggunakan hashing untuk memeriksa perubahan file selama komit tetapi mereka menggunakan SHA1.
JimHough
3
@ BeowulfNode42 Itulah sebabnya saya mengawali komentar saya dengan "Kecuali Anda khawatir tentang seseorang yang sengaja mencoba membuat tabrakan"
Ajedi32
5

Karena saya payah dan tidak memiliki poin reputasi yang cukup, saya tidak dapat menambahkan berita gembira ini sebagai komentar.

Tapi, jika Anda akan menggunakan cmpperintah (dan tidak perlu / ingin menjadi verbose) Anda bisa ambil status keluar. Per cmphalaman manual:

Jika FILE '-' atau hilang, baca input standar. Status keluar adalah 0 jika input sama, 1 jika berbeda, 2 jika bermasalah.

Jadi, Anda bisa melakukan sesuatu seperti:

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
Gregory Martin
sumber
ya, tapi ini sebenarnya cara yang lebih rumit untuk melakukan cmp --silent $FILE1 $FILE2 ; if [ "$?" == "1" ]; then echo "files differ"; fiyang pada gilirannya merupakan cara yang lebih rumit untuk dilakukan cmp --silent $FILE1 $FILE2 || echo "files differ"karena Anda dapat menggunakan perintah dalam ekspresi secara langsung. Ini menggantikan $?. Alhasil status perintah yang ada akan dibandingkan. Dan itulah yang dilakukan jawaban lainnya. btw. Jika seseorang kesulitan --silent, itu tidak didukung di mana-mana (busybox). gunakan-s
papo
4

Untuk file yang tidak berbeda, metode apa pun akan mengharuskan membaca kedua file sepenuhnya, bahkan jika membaca dulu.

Tidak ada alternatif. Jadi membuat hashes atau checksum di beberapa titik waktu memerlukan membaca seluruh file. File besar membutuhkan waktu.

Pengambilan file metadata jauh lebih cepat daripada membaca file besar.

Jadi, adakah metadata file yang dapat Anda gunakan untuk memastikan bahwa file-file itu berbeda? Ukuran file ? atau bahkan hasil dari perintah file yang hanya membaca sebagian kecil file?

Ukuran file contoh kode fragmen:

  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'  

Jika ukuran file yang sama maka Anda terjebak dengan membaca file lengkap.

jim mcnamara
sumber
1
Gunakan ls -nuntuk menghindari masalah jika nama pengguna atau grup memiliki spasi putih.
tricasse
2

Coba juga untuk menggunakan perintah 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

Perintah cksum akan menampilkan jumlah byte file. Lihat 'man cksum'.

Nono Taps
sumber
2
Itu juga pikiran pertamaku. Namun, hash masuk akal jika Anda harus membandingkan file yang sama berkali-kali, karena hash dihitung hanya sekali. Jika Anda membandingkannya hanya sekali, tetap md5membaca seluruh file, jadi cmp, berhenti pada perbedaan pertama, akan jauh lebih cepat.
Francesco Dondi
0

Melakukan beberapa pengujian dengan Raspberry Pi 3B + (Saya menggunakan sistem file overlay, dan perlu disinkronkan secara berkala), saya menjalankan perbandingan sendiri untuk diff -q dan cmp -s; perhatikan bahwa ini adalah log dari dalam / dev / shm, jadi kecepatan akses disk adalah bukan masalah:

[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

Saya menjalankannya beberapa kali. cmp -s secara konsisten memiliki waktu yang sedikit lebih pendek pada kotak tes yang saya gunakan. Jadi jika Anda ingin menggunakan cmp -s untuk melakukan hal-hal antara dua file ....

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"
Jack Simth
sumber