Linux: belirli bir klasör ve içerik için tek bir karma hesaplama?


98

Elbette bunu kolayca yapmanın bir yolu olmalı!

Ben Linux komut satırı gibi uygulamalar denedim sha1sumve md5sumancak sadece dosyaları tek tek ve çıkış karma değerlerin listesi, her dosya için bir karma değerlerini hesaplayıp edebilmek görünmektedir.

Bir klasörün tüm içeriği için (sadece dosya adları değil) tek bir karma oluşturmam gerekiyor.

Gibi bir şey yapmak isterim

sha1sum /folder/of/stuff > singlehashvalue

Düzenleme: Açıklığa kavuşturmak gerekirse, dosyalarım bir dizin ağacında birden çok seviyede, hepsi aynı kök klasörde oturmuyor.


1
'Tüm içerikler' derken, dizindeki tüm dosyaların mantıksal verilerini mi yoksa kök karma değerine ulaşırken meta ile birlikte verilerini mi kastediyorsunuz? Kullanım durumunuzun seçim kriterleri oldukça geniş olduğundan, cevabımda birkaç pratik olanı ele almaya çalıştım.
altı k

Yanıtlar:


124

Olası bir yol şudur:

sha1sum yol / klasör / * | sha1sum

Tam bir dizin ağacı varsa, muhtemelen find ve xargs'ı kullanmanız daha iyidir. Olası bir komut şöyle olacaktır:

/ klasörün yolunu bul -tip f -print0 | sırala -z | xargs -0 sha1sum | sha1sum

Ve son olarak, izinleri ve boş dizinleri de hesaba katmanız gerekirse:

(find path/to/folder -type f -print0  | sort -z | xargs -0 sha1sum;
 find path/to/folder \( -type f -o -type d \) -print0 | sort -z | \
   xargs -0 stat -c '%n %a') \
| sha1sum

Argümanlar statdosyanın adını ve ardından sekizlik izinlerini yazdırmasına neden olur. İki buluntu birbiri ardına çalışacak ve iki kat disk IO miktarına neden olacak, ilki tüm dosya adlarını bulup içeriği sağlama, ikincisi tüm dosya ve dizin adlarını, yazdırma adı ve modu. Daha küçük bir sağlama toplamı için "dosya adları ve sağlama toplamları" listesi ve ardından "izinlere sahip adlar ve dizinler" sağlama toplamı alınır.


2
ve LC_ALL = POSIX ayarlamayı unutmayın, böylece çeşitli araçlar yerel ayardan bağımsız çıktı oluşturur.
David Schmitt

2
Kedi buldum | sha1sum'un sha1sum'dan çok daha hızlı olması | sha1sum. YMMV, sisteminizde şunların her birini deneyin: zaman yolu bulma / / klasör-türü f -print0 | sırala -z | xargs -0 sha1sum | sha1sum; zaman yolu bulma / klasör-türü f -print0 | sırala -z | xargs -0 kedi | sha1sum
Bruno Bronosky

5
@RichardBronosky - İki dosyamız olduğunu varsayalım, A ve B. A "foo" ve B "bar buradaydı" içeriyor. Yönteminizle, bunu, C'nin "foobar" içerdiği ve D'nin "buradaydı" içerdiği iki C ve D dosyasından ayıramayız. Her dosyayı ayrı ayrı hashing ve ardından tüm "dosya adı hash" çiftlerini hash ederek farkı görebiliriz.
Vatine

2
Bunun dizin yolundan bağımsız olarak çalışmasını sağlamak için (yani iki farklı klasörün karmalarını karşılaştırmak istediğinizde), göreceli bir yol kullanmanız ve uygun dizine geçmeniz gerekir, çünkü yollar son karmaya dahil edilmiştir:find ./folder -type f -print0 | sort -z | xargs -0 sha1sum | sha1sum
robbles

3
@robbles Bu doğru ve neden bitin /üzerine bir baş harfini koymadım path/to/folder.
Vatine

26
  • Yardımcı gibi bir dosya sistemi saldırı tespit aracı kullanın .

  • dizinin katran topunu hash edin:

    tar cvf - /path/to/folder | sha1sum

  • Vatine'nin oneliner'ı gibi bir şeyi kendiniz kodlayın :

    find /path/to/folder -type f -print0 | sort -z | xargs -0 sha1sum | sha1sum


3
Katran çözümü için +1. Bu en hızlısıdır, ancak v. Ayrıntıyı düşürmek yalnızca onu yavaşlatır.
Bruno Bronosky

7
tar çözümünün, dosyaları karşılaştırdığınızda dosyaların aynı sırada olduğunu varsaydığını unutmayın. Karşılaştırma yapılırken dosyaların bulunduğu dosya sistemine bağlı olup olmadıkları.
no

5
Git karması bu amaç için uygun değildir, çünkü dosya içerikleri yalnızca girdisinin bir parçasıdır. Bir dalın ilk yürütmesi için bile, hash, commit mesajından ve commit meta verilerinden de etkilenir, örneğin commit zamanı. Aynı dizin yapısını birden çok kez kaydederseniz, her seferinde farklı bir karma elde edersiniz, bu nedenle ortaya çıkan karma, iki dizinin birbirinin tam kopyası olup olmadığını yalnızca karmayı göndererek belirlemek için uygun değildir.
Zoltan

1
@Zoltan git karması mükemmel bir şekilde, bir ağaç karması kullanırsanız ve bir commit karması kullanırsanız.
hobbs

1
@hobbs Cevap orijinal olarak "commit hash" olarak belirtildi ve bu kesinlikle bu amaca uygun değil. Ağaç karması kulağa çok daha iyi bir aday gibi geliyor, ancak yine de gizli tuzaklar olabilir. Aklıma gelenlerden biri, bazı dosyalarda çalıştırılabilir bit setinin ağaç hashini değiştirmesidir. Bundan git config --local core.fileMode falsekaçınmak için taahhütte bulunmadan önce yayınlamalısınız . Bunun gibi başka uyarılar var mı bilmiyorum.
Zoltan

14

Yapabilirsin tar -c /path/to/folder | sha1sum


17
Bu sağlama toplamını farklı bir makinede çoğaltmak istiyorsanız, biçim belirsizlik için yer olduğu ve birçok sürümde mevcut olduğu için tar iyi bir seçim olmayabilir, bu nedenle başka bir makinedeki tar aynı dosyalardan farklı çıktılar üretebilir.
slowdog

2
slowdog en geçerli kaygılar rağmen dosya içeriğinin, izinleri vb umurumda değil değiştirme zamanı, eklemek eğer --mtimeşöyle seçeneği: tar -c /path/to/folder --mtime="1970-01-01" | sha1sum.
Binary Phile

@ S.Lott dizin boyutu büyükse, yani dizinin boyutu çok büyükse, onu sıkıştırmak ve
md5'i

13

Klasördeki bir şeyin değişip değişmediğini kontrol etmek istiyorsanız, bunu tavsiye ederim:

ls -alR --full-time /folder/of/stuff | sha1sum

Size klasörleri, alt klasörleri, dosyalarını, zaman damgalarını, boyutlarını ve izinlerini içeren ls çıktısının bir karmasını verecektir. Bir şeyin değişip değişmediğini belirlemeniz gereken hemen hemen her şey.

Lütfen bu komutun her dosya için hash oluşturmayacağını unutmayın, ancak bu yüzden find'ı kullanmaktan daha hızlı olması gerektiğini unutmayın.


1
Çözümün basitliği göz önüne alındığında, bunun neden daha fazla olumlu oyu olmadığından emin değilim. Bunun neden işe yaramadığını kimse açıklayabilir mi?
Dave C

1
Ben oluşturulan karma vb dosya sahibi, tarih formatlı kurulumu dayalı olacak şekilde bu ideal değildir herhalde
Ryouta

1
Ls komutu, istediğinizi çıktı almak için özelleştirilebilir. Grubu ve sahibi atlamak için -l'yi -gG ile değiştirebilirsiniz. Ve tarih formatını --time-style seçeneğiyle değiştirebilirsiniz. Temel olarak ls man sayfasına bakın ve ihtiyaçlarınıza uygun olanı görün.
Shumoapp

@DaveC Çünkü oldukça faydasız. Dosya adlarını karşılaştırmak istiyorsanız, onları doğrudan karşılaştırmanız yeterlidir. O kadar büyük değiller.
Navin

7
@Navin Sorudan, dosya içeriklerine hashing uygulamasının mı yoksa bir ağaçta değişiklik mi saptamanın gerekli olduğu açık değil. Her vakanın kendi kullanımları vardır. Örneğin, bir çekirdek ağacında 45K dosya adı saklamak, tek bir hash'den daha az pratiktir. ls -lAgGR --block-size = 1 --time-style = +% s | sha1sum benim için harika çalışıyor
yashma

5

Sağlam ve temiz bir yaklaşım

  • İlk önce, mevcut hafızayı meşgul etmeyin ! Tüm dosyayı beslemek yerine bir dosyayı parçalar halinde karma haline getirin.
  • Farklı ihtiyaçlar / amaçlar için farklı yaklaşımlar (aşağıdakilerin tümü veya uygun olanı seçin):
    • Dizin ağacındaki tüm girişlerin yalnızca giriş adını karma hale getirin
    • Tüm girişlerin dosya içeriğini karma haline getirin (meta, inode numarası, ctime, atime, mtime, boyut vb. Gibi bırakarak, fikri anlarsınız)
    • Sembolik bir bağ için içeriği, referans adıdır. Karıştırın veya atlamayı seçin
    • Girişin içeriğine hashing uygularken sembolik bağı takip edin veya takip etmeyin (çözülen ad)
    • Bir dizin ise, içeriği yalnızca dizin girdileridir. Özyinelemeli olarak dolaşırken, sonunda karma işlemi uygulanacaktır, ancak bu seviyenin dizin girişi adlarının bu dizini etiketlemek için karma hale getirilmesi gerekir mi? İçerikleri derinlemesine incelemek zorunda kalmadan bir değişikliği hızlı bir şekilde tanımlamak için karmanın gerekli olduğu kullanım durumlarında faydalıdır. Bir örnek, bir dosyanın adı değişiklikleri olabilir, ancak içeriğin geri kalanı aynı kalır ve hepsi oldukça büyük dosyalardır.
    • Büyük dosyaları iyi yönetin (yine RAM'e dikkat edin)
    • Çok derin dizin ağaçlarını işleyin (açık dosya tanımlayıcılarına dikkat edin)
    • Standart olmayan dosya adlarını işleyin
    • Soket, boru / FIFO, blok aygıt, karakter aygıtı gibi dosyalarla nasıl devam edilir? Onları da hash etmek zorunda mı?
    • Geçiş yaparken herhangi bir girişin erişim zamanını güncellemeyin çünkü bu, belirli kullanım durumları için bir yan etki ve ters etki (sezgisel?) Olacaktır.

Başımın üstünde olan şey bu, bunun üzerinde pratik olarak biraz zaman harcayan herhangi biri, başka sorunlara ve köşe vakalarına yakalanmış olurdu.

İşte , hafızaya çok az önem veren ve çoğu duruma hitap eden bir araç , sınırlarda biraz kaba olabilir, ancak oldukça yardımcı olmuştur.

Örnek bir kullanım ve çıktı dtreetrawl.

Usage:
  dtreetrawl [OPTION...] "/trawl/me" [path2,...]

Help Options:
  -h, --help                Show help options

Application Options:
  -t, --terse               Produce a terse output; parsable.
  -j, --json                Output as JSON
  -d, --delim=:             Character or string delimiter/separator for terse output(default ':')
  -l, --max-level=N         Do not traverse tree beyond N level(s)
  --hash                    Enable hashing(default is MD5).
  -c, --checksum=md5        Valid hashing algorithms: md5, sha1, sha256, sha512.
  -R, --only-root-hash      Output only the root hash. Blank line if --hash is not set
  -N, --no-name-hash        Exclude path name while calculating the root checksum
  -F, --no-content-hash     Do not hash the contents of the file
  -s, --hash-symlink        Include symbolic links' referent name while calculating the root checksum
  -e, --hash-dirent         Include hash of directory entries while calculating root checksum

İnsan dostu çıktıdan bir pasaj:

...
... //clipped
...
/home/lab/linux-4.14-rc8/CREDITS
        Base name                    : CREDITS
        Level                        : 1
        Type                         : regular file
        Referent name                :
        File size                    : 98443 bytes
        I-node number                : 290850
        No. directory entries        : 0
        Permission (octal)           : 0644
        Link count                   : 1
        Ownership                    : UID=0, GID=0
        Preferred I/O block size     : 4096 bytes
        Blocks allocated             : 200
        Last status change           : Tue, 21 Nov 17 21:28:18 +0530
        Last file access             : Thu, 28 Dec 17 00:53:27 +0530
        Last file modification       : Tue, 21 Nov 17 21:28:18 +0530
        Hash                         : 9f0312d130016d103aa5fc9d16a2437e

Stats for /home/lab/linux-4.14-rc8:
        Elapsed time     : 1.305767 s
        Start time       : Sun, 07 Jan 18 03:42:39 +0530
        Root hash        : 434e93111ad6f9335bb4954bc8f4eca4
        Hash type        : md5
        Depth            : 8
        Total,
                size           : 66850916 bytes
                entries        : 12484
                directories    : 763
                regular files  : 11715
                symlinks       : 6
                block devices  : 0
                char devices   : 0
                sockets        : 0
                FIFOs/pipes    : 0

1
Bir klasörün sağlam ve temiz bir sha256'sını elde etmek için kısa bir örnek verebilir misiniz, belki de üç alt dizini ve her birinde birkaç dosya bulunan bir Windows klasörü için?
Ferit

3

Dosya adlarını görmezden gelerek sadece dosyaların içeriğine hashing uygulamak istiyorsanız şunu kullanabilirsiniz:

cat $FILES | md5sum

Hash'i hesaplarken dosyaların aynı sırada olduğundan emin olun:

cat $(echo $FILES | sort) | md5sum

Ancak dosya listenizde dizin olamaz.


2
Bir dosyanın sonunu, onu alfabetik olarak izleyen dosyanın başlangıcına taşımak, karmayı etkilemez, ancak gerekir. Karmaya bir dosya sınırlayıcının veya dosya uzunluklarının dahil edilmesi gerekir.
Jason Stangroome

3

Bunu başarmak için başka bir araç:

http://md5deep.sourceforge.net/

Sesler olduğu gibi: md5sum gibi ama aynı zamanda özyinelemeli ve diğer özellikler.


1
Bu bağlantı soruyu cevaplayabilirken, cevabın temel kısımlarını buraya eklemek ve referans için bağlantıyı sağlamak daha iyidir. Bağlantılı sayfa değişirse yalnızca bağlantı yanıtları geçersiz hale gelebilir.
Mamoun Benghezal

3

Bu bir git deposuysa ve içindeki tüm dosyaları yok saymak istiyorsanız .gitignore, bunu kullanmak isteyebilirsiniz:

git ls-files <your_directory> | xargs sha256sum | cut -d" " -f1 | sha256sum | cut -d" " -f1

Bu benim için iyi çalışıyor.


Çok teşekkürler! :)
visortelle

Birçok uygulama için bu yaklaşım üstündür. Sadece kaynak kod dosyalarının karma işlemi, çok daha kısa sürede yeterince benzersiz bir karma elde eder.
John McGehee


2

Dosya değişiklikleri için tüm dizini kontrol etmem gerekiyordu.

Ancak, zaman damgaları, dizin sahiplikleri hariç tutulduğunda.

Hedef, dosyalar aynıysa, her yerde aynı olan bir toplamı elde etmektir.

Dosyalardan başka hiçbir şeye bakılmaksızın veya bunlarda bir değişiklik olursa, diğer makinelerde barındırılan dahil.

md5sum * | md5sum | cut -d' ' -f1

Dosyaya göre bir karma listesi oluşturur, ardından bu karmaları tek bir karma halinde birleştirir.

Bu, katran yönteminden çok daha hızlıdır.

Bir İçin güçlü gizlilik bizim sağlamalarının içinde kullanabileceğimiz sha512sum aynı tarifi üzerine.

sha512sum * | sha512sum | cut -d' ' -f1

Karmalar da sha512sum kullanan her yerde aynıdır , ancak bunu tersine çevirmenin bilinen bir yolu yoktur.


Bu, bir dizine hashing uygulamak için kabul edilen cevaptan çok daha basit görünüyor. Kabul edilen cevabı güvenilir bulmuyordum. Bir sorun ... hash'lerin farklı bir sırada çıkma şansı var mı? sha256sum /tmp/thd-agent/* | sortgüvenilir bir sipariş için denediğim şey, sonra sadece hashing.
thinktt

Merhaba, karmalar varsayılan olarak alfabetik sırada geliyor gibi görünüyor. Güvenilir sipariş ile neyi kastediyorsunuz? Tüm bunları kendi başına düzenlemelisin. Örneğin ilişkilendirilebilir diziler kullanmak, giriş + karma. Daha sonra bu diziyi girişe göre sıralarsınız, bu, sıralama düzeninde hesaplanan karmalar listesi verir. Aksi takdirde bir json nesnesini kullanabileceğinizi ve tüm nesneyi doğrudan hash edebileceğinizi düşünüyorum.
NVRM

Anladıysam, dosyaların alfabetik sıraya göre hash hale getirdiğini söylüyorsunuz. Bu doğru görünüyor. Yukarıdaki kabul edilen cevapta yer alan bir şey bazen bana aralıklı olarak farklı siparişler veriyordu, bu yüzden sadece bunun bir daha olmayacağından emin olmaya çalışıyorum. Sonuna sıralama koymaya devam edeceğim. Çalışıyor gibi görünüyor. Gördüğüm bu yöntemle kabul edilen cevapla ilgili tek sorun, iç içe geçmiş klasörlerle ilgilenmiyor. Benim durumumda herhangi bir klasörüm yok, bu yüzden bu harika çalışıyor.
thinktt

peki ya ls -r | sha256sum?
NVRM

@NVRM bunu denedi ve dosya içeriğini değil dosya adı değişikliklerini kontrol etti
Gi0rgi0s

1

İki adımda yapmaya çalışın:

  1. bir klasördeki tüm dosyalar için karmalar içeren bir dosya oluşturun
  2. bu dosyayı hashleyin

Şöyle:

# for FILE in `find /folder/of/stuff -type f | sort`; do sha1sum $FILE >> hashes; done
# sha1sum hashes

Veya hepsini aynı anda yapın:

# cat `find /folder/of/stuff -type f | sort` | sha1sum

for F in 'find ...' ...isimlerde boşluk olduğunda çalışmaz (bugünlerde her zaman yaptığınız gibi).
mivk

1

Tek tek dosyalar için sonuçları sort(karmayı değiştirmek için dosyaların yeniden sıralanmasını önlemek için) md5sumveya sha1sumhangisini seçerseniz seçin.


1

Bunu yapmak için bir Groovy senaryosu yazdım:

import java.security.MessageDigest

public static String generateDigest(File file, String digest, int paddedLength){
    MessageDigest md = MessageDigest.getInstance(digest)
    md.reset()
    def files = []
    def directories = []

    if(file.isDirectory()){
        file.eachFileRecurse(){sf ->
            if(sf.isFile()){
                files.add(sf)
            }
            else{
                directories.add(file.toURI().relativize(sf.toURI()).toString())
            }
        }
    }
    else if(file.isFile()){
        files.add(file)
    }

    files.sort({a, b -> return a.getAbsolutePath() <=> b.getAbsolutePath()})
    directories.sort()

    files.each(){f ->
        println file.toURI().relativize(f.toURI()).toString()
        f.withInputStream(){is ->
            byte[] buffer = new byte[8192]
            int read = 0
            while((read = is.read(buffer)) > 0){
                md.update(buffer, 0, read)
            }
        }
    }

    directories.each(){d ->
        println d
        md.update(d.getBytes())
    }

    byte[] digestBytes = md.digest()
    BigInteger bigInt = new BigInteger(1, digestBytes)
    return bigInt.toString(16).padLeft(paddedLength, '0')
}

println "\n${generateDigest(new File(args[0]), 'SHA-256', 64)}"

Her dosyanın yazdırılmasını önlemek, ileti özetini değiştirmek, dizin karmasını çıkarmak vb. İçin kullanımı özelleştirebilirsiniz. NIST test verilerine karşı test ettim ve beklendiği gibi çalışıyor. http://www.nsrl.nist.gov/testdata/

gary-macbook:Scripts garypaduana$ groovy dirHash.groovy /Users/garypaduana/.config
.DS_Store
configstore/bower-github.yml
configstore/insight-bower.json
configstore/update-notifier-bower.json
filezilla/filezilla.xml
filezilla/layout.xml
filezilla/lockfile
filezilla/queue.sqlite3
filezilla/recentservers.xml
filezilla/sitemanager.xml
gtk-2.0/gtkfilechooser.ini
a/
configstore/
filezilla/
gtk-2.0/
lftp/
menus/
menus/applications-merged/

79de5e583734ca40ff651a3d9a54d106b52e94f1f8c2cd7133ca3bbddc0c6758

0

Sen olabilir sha1sumsonra karma değerlerinin listesini oluşturmak ve sha1sumliste yine, bunu başarmak istediğiniz tam olarak ne bağlıdır.


0

İşte Python 3'ün küçük boyutlu dosyalar için iyi çalışan basit, kısa bir çeşidi (örneğin, her dosyanın ayrı ayrı RAM'e kolayca sığabileceği bir kaynak ağacı veya başka bir şey), diğer çözümlerin fikirlerine dayanarak boş dizinleri yok sayarak:

import os, hashlib

def hash_for_directory(path, hashfunc=hashlib.sha1):                                                                                            
    filenames = sorted(os.path.join(dp, fn) for dp, _, fns in os.walk(path) for fn in fns)         
    index = '\n'.join('{}={}'.format(os.path.relpath(fn, path), hashfunc(open(fn, 'rb').read()).hexdigest()) for fn in filenames)               
    return hashfunc(index.encode('utf-8')).hexdigest()                          

Şu şekilde çalışır:

  1. Dizindeki tüm dosyaları yinelemeli olarak bulun ve ada göre sıralayın
  2. Her dosyanın karmasını (varsayılan: SHA-1) hesaplayın (tüm dosyayı belleğe okur)
  3. "Filename = hash" satırlarıyla metinsel bir dizin oluşturun
  4. Bu dizini UTF-8 baytlık bir dizeye kodlayın ve

SHA-1 sizin fincan çayınız değilse , farklı bir hash fonksiyonunu ikinci parametre olarak geçirebilirsiniz .


0

Şimdiye kadar bunu yapmanın en hızlı yolu hala katran kullanmaktır. Ve birkaç ek parametre ile, meta verilerin neden olduğu farktan da kurtulabiliriz.

Dizin için tar kullanmak için , yolu tar sırasında sıraladığınızdan emin olmanız gerekir , aksi takdirde her zaman farklıdır.

tar -C <root-dir> -cf - --sort=name <dir> | sha256sum

zamanı görmezden gel

Erişim zamanını veya değiştirme zamanını --mtime='UTC 2019-01-01' önemsemiyorsanız, tüm zaman damgalarının aynı olduğundan emin olmak gibi bir şey de kullanın .

sahipliği görmezden gel

Genellikle --group=0 --owner=0 --numeric-ownersahip meta verilerini birleştirmek için eklememiz gerekir .

bazı dosyaları yok say

kullanım --exclude=PATTERN

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.