Unix / Linux'ta iki dosyanın aynı içeriğe sahip olup olmadığını anlamanın en hızlı yolu?


231

İçinde iki dosya aynı veri içerip içermediğini kontrol etmek için gereken bir kabuk komut dosyası var. Bunu bir çok dosya için yapıyorum ve diffkomut dosyamda komut performans darboğazı gibi görünüyor.

İşte çizgi:

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

if ($status) then ...

Dosyaları karşılaştırmanın daha hızlı bir yolu olabilir diffmi , varsayılan yerine özel bir algoritma olabilir mi?


10
Bu gerçekten çirkin, ama iki dosyanın aynı olup olmadığını görmek istemiyorsunuz, iki dosyanın aynı içeriğe sahip olup olmadığını soruyorsunuz. Aynı dosyalar aynı inode'lara (ve aynı cihaza) sahiptir.
Zano

1
Kabul edilen cevaptan farklı olarak, bu cevaptaki ölçüm ile diffve arasında önemli bir fark tanımamaktadır cmp.
wedi

Yanıtlar:


388

cmpİlk bayt farkında duracağına inanıyorum :

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

1
Bir komuttan daha fazla komutu nasıl ekleyebilirim? Bir dosyayı kopyalamak ve yeniden başlatmak istiyorum.
feedc0de

9
cmp -s $old $newayrıca çalışır. -siçin kısa--silent
Rohmer

7
Bir hız artışı olarak, içeriği karşılaştırmadan önce dosya boyutlarının eşit olup olmadığını kontrol etmelisiniz. Herkes cmp bunu yapar biliyor mu?
BeowulfNode42

3
Birden çok komut çalıştırmak için parantez kullanabilirsiniz: cmp -s old new || {yankı değil; yankı; yankı aynı; }
unfa

6
@ BeowulfNode42 evet, herhangi bir iyi uygulama cmpönce dosya boyutunu kontrol edecektir. GNU sürümü, içerdiği ek optimizasyonları görmek istiyorsanız: git.savannah.gnu.org/cgit/diffutils.git/tree/src/cmp.c
Ryan Graham

53

@Alex Howansky bunun için 'cmp --silent' kullandım. Ama hem olumlu hem de olumsuz yanıta ihtiyacım var, bu yüzden kullanıyorum:

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

Daha sonra sabit bir dosyaya karşı dosyaları kontrol etmek için terminalde veya ssh ile çalıştırabilirim.


16
Senin Eğer echo successkomut (veya onun yerine koymak başka türlü komutu) başarısız, senin "negatif tepki" komutu çalışacaktır. Bir "if-then-else-fi" yapısı kullanmalısınız. Örneğin, bu basit örnek gibi .
Wildcard

18

Neden her iki dosya içeriğinin karmasını almıyorsunuz?

Bu komut dosyasını deneyin, örneğin script.sh dosyasını arayın ve aşağıdaki gibi çalıştırın: 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 Bunun nedeni karma algoritmaların bire bir olmamasıdır . Karma alanı geniş olacak ve farklı girdilerin farklı karma üretme şansı yüksek olacak şekilde tasarlanmıştır. Gerçek şu ki, karma alan sonlu iken, karma için olası dosyalar aralığı değil - sonunda bir çarpışma olacak. Kriptolojide buna Doğum Günü Saldırısı denir .
will

5
@ Eh, etkili bir şekilde çalışması garanti edilir. Çalışmadığı ihtimaller, matematiksel olarak, etrafta 1/(2^511). Kasıtlı olarak bir çarpışma yaratmaya çalışan biri hakkında endişelenmedikçe , yanlış pozitif üreten bu yöntemin fikri gerçekten ciddi bir endişe değildir. cmpyine de daha verimlidir, çünkü dosyaların eşleşmemesi durumunda tüm dosyayı okumak zorunda değildir.
Ajedi32

12
OP HIZLI yol istedi ... ilk eşleşmeyen bit (cmp kullanarak) tüm dosya, özellikle dosyaları büyükse karma daha hızlı (cmp kullanarak) aramak olmaz?
KoZm0kNoT

3
md5, bire çok karşılaştırma yapıyorsanız en iyisidir. Md5 karmasını bir öznitelik olarak veya her dosyaya karşı bir veritabanında depolayabilirsiniz. Yeni bir dosya görünürse ve aynı dosyanın dosya sisteminde herhangi bir yerde olup olmadığını kontrol etmeniz gerekiyorsa, tek yapmanız gereken yeni dosyanın karmasını hesaplamak ve öncekilere karşı kontrol etmektir. Git'in bir işlem sırasında dosya değişikliklerini kontrol etmek için karma kullandığından eminim ancak SHA1 kullanıyorlar.
JimHough

3
@ BeowulfNode42 Bu yüzden "kasıtlı olarak bir çarpışma yaratmaya çalışan biri hakkında endişelenmedikçe"
yorumumu önceden belirledim

5

Berbat olduğum için ve yeterince itibar puanım olmadığı için bu tidbit'i yorum olarak ekleyemiyorum.

Ancak, cmpkomutu kullanacaksanız (ve ayrıntılı / zorunlu olmak istemiyorsanız) çıkış durumunu alabilirsiniz. Başına cmpadam sayfası:

Bir DOSYA '-' veya eksikse, standart girişi okuyun. Girişler aynıysa çıkış durumu 0, farklıysa 1, sorun varsa 2'dir.

Yani, şöyle bir şey yapabilirsiniz:

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

evet, ama bu aslında daha karmaşık bir yöntemdir, cmp --silent $FILE1 $FILE2 ; if [ "$?" == "1" ]; then echo "files differ"; fibu da daha karmaşık bir yöntemdir cmp --silent $FILE1 $FILE2 || echo "files differ"çünkü doğrudan ifadede komutu kullanabilirsiniz. Bunun yerine geçer $?. Sonuç olarak komutun mevcut durumu karşılaştırılacaktır. Ve diğer cevap da bunu yapıyor. Btw. Birisi mücadele ediyorsa --silent, her yerde desteklenmez (meşgul kutusu). kullanım-s
papo

4

Farklı olmayan dosyalar için, herhangi bir yöntem, okuma geçmişte olsa bile her iki dosyayı da tamamen okumayı gerektirir.

Alternatif yok. Bu nedenle, zaman içinde karma veya sağlama toplamları oluşturmak için tüm dosyanın okunması gerekir. Büyük dosyalar zaman alır.

Dosya meta verilerinin alınması büyük bir dosyayı okumaktan çok daha hızlıdır.

Peki, dosyaların farklı olduğunu belirlemek için kullanabileceğiniz herhangi bir dosya meta verisi var mı? Dosya boyutu ? hatta dosyanın sadece küçük bir bölümünü okuyan dosya komutunun sonuçları?

Dosya boyutu örnek kod parçası:

  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'  

Dosyalar aynı boyutta ise, tam dosya okumaları ile sıkışıp kalırsınız.


1
ls -nKullanıcı veya grup adlarında boşluk varsa sorunları önlemek için kullanın .
tricasse

2

Ayrıca cksum komutunu kullanmayı deneyin:

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 komutu bir dosyanın bayt sayısını çıktılar. Bkz. 'Man cksum'.


2
Benim de ilk fikrim buydu. Ancak, hash yalnızca bir kez hesaplandığından, aynı dosyayı birçok kez karşılaştırmanız gerekiyorsa hash'ler mantıklıdır. Yalnızca bir kez karşılaştırırsanız md5, tüm dosyayı yine de cmpokursanız, ilk farkta durmak çok daha hızlı olacaktır.
Francesco Dondi

0

Raspberry Pi 3B + ile bazı testler yapıyorum (bindirme dosya sistemi kullanıyorum ve periyodik olarak senkronize etmem gerekiyor), diff -q ve cmp -s için kendi karşılaştırmamı yaptım; bunun / dev / shm içindeki bir günlük olduğunu unutmayın, bu nedenle disk erişim hızları sorun değildir:

[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

Birkaç kez koştum. cmp -s kullandığım test kutusunda sürekli olarak biraz daha kısa süreleri vardı. Yani iki dosya arasında bir şeyler yapmak için cmp -s kullanmak istiyorsanız ....

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"
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.