Linux: menghitung satu hash untuk folder & konten tertentu?


98

Pasti ada cara untuk melakukannya dengan mudah!

Saya telah mencoba aplikasi baris perintah Linux seperti sha1sumdan md5sumtetapi mereka tampaknya hanya dapat menghitung hash file individual dan mengeluarkan daftar nilai hash, satu untuk setiap file.

Saya perlu membuat satu hash untuk seluruh konten folder (bukan hanya nama file).

Saya ingin melakukan sesuatu seperti

sha1sum /folder/of/stuff > singlehashvalue

Sunting: untuk memperjelas, file saya ada di beberapa tingkatan dalam pohon direktori, mereka tidak semua berada di folder root yang sama.


1
Yang Anda maksud dengan 'seluruh konten' adalah data logis dari semua file di direktori atau datanya bersama dengan meta saat sampai di hash root? Karena kriteria pemilihan kasus penggunaan Anda cukup luas, saya telah mencoba membahas beberapa yang praktis dalam jawaban saya.
enam-k

Jawaban:


124

Salah satu cara yang mungkin adalah:

jalur sha1sum / ke / folder / * | sha1sum

Jika ada seluruh pohon direktori, Anda mungkin lebih baik menggunakan find dan xargs. Satu perintah yang mungkin adalah

temukan jalur / ke / folder -type f -print0 | urutkan -z | xargs -0 sha1sum | sha1sum

Dan, terakhir, jika Anda juga perlu mempertimbangkan izin dan direktori kosong:

(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

Argumen untuk statakan menyebabkannya mencetak nama file, diikuti dengan izin oktalnya. Kedua penemuan akan berjalan satu demi satu, menyebabkan dua kali jumlah IO disk, yang pertama menemukan semua nama file dan memeriksa isinya, yang kedua menemukan semua nama file dan direktori, nama dan mode pencetakan. Daftar "nama file dan checksum", diikuti dengan "nama dan direktori, dengan izin" kemudian akan di-checksum, untuk checksum yang lebih kecil.


2
dan jangan lupa untuk menyetel LC_ALL = POSIX, sehingga berbagai alat membuat keluaran independen lokal.
David Schmitt

2
Saya menemukan kucing | sha1sum menjadi jauh lebih cepat dari sha1sum | sha1sum. YMMV, coba masing-masing ini di sistem Anda: waktu temukan jalur / ke / folder -type f -print0 | urutkan -z | xargs -0 sha1sum | sha1sum; waktu menemukan jalur / ke / folder -type f -print0 | urutkan -z | xargs -0 kucing | sha1sum
Bruno Bronosky

5
@RichardBronosky - Mari kita asumsikan kita memiliki dua file, A dan B. A berisi "foo" dan B berisi "bar was here". Dengan metode Anda, kami tidak akan dapat memisahkannya dari dua file C dan D, di mana C berisi "foobar" dan D berisi "was here". Dengan melakukan hashing setiap file satu per satu dan kemudian melakukan hash semua pasangan "hash nama file", kita dapat melihat perbedaannya.
Vatine

2
Untuk membuat ini bekerja terlepas dari jalur direktori (yaitu ketika Anda ingin membandingkan hash dari dua folder yang berbeda), Anda perlu menggunakan jalur relatif dan mengubah ke direktori yang sesuai, karena jalur tersebut disertakan dalam hash terakhir:find ./folder -type f -print0 | sort -z | xargs -0 sha1sum | sha1sum
robbles

3
@ Robbles Itu benar dan mengapa saya tidak membuat inisial /di path/to/folderbit.
Vatine

26
  • Gunakan alat deteksi intrusi sistem file seperti ajudan .

  • hash bola tar direktori:

    tar cvf - /path/to/folder | sha1sum

  • Kode sesuatu sendiri, seperti oneliner vatine :

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


3
1 untuk solusi tar. Itu yang tercepat, tetapi hilangkan v. Verbositas hanya memperlambatnya.
Bruno Bronosky

7
Perhatikan bahwa solusi tar mengasumsikan file-file tersebut berada dalam urutan yang sama ketika Anda membandingkannya. Apakah mereka akan bergantung pada sistem file tempat file berada saat melakukan perbandingan.
no

5
Git hash tidak cocok untuk tujuan ini karena konten file hanya sebagian dari inputnya. Bahkan untuk komit awal suatu cabang, hash dipengaruhi oleh pesan komit dan metadata komit juga, seperti waktu komit. Jika Anda melakukan struktur direktori yang sama beberapa kali, Anda akan mendapatkan hash yang berbeda setiap saat, sehingga hasil hash tidak cocok untuk menentukan apakah dua direktori adalah salinan persis satu sama lain dengan hanya mengirimkan hash tersebut.
Zoltan

1
@Zoltan hash git baik-baik saja, jika Anda menggunakan hash pohon dan bukan hash komit.
Hobbs

1
@hobbs Jawaban aslinya menyatakan "commit hash", yang tentunya tidak cocok untuk tujuan ini. Hash pohon terdengar seperti kandidat yang jauh lebih baik, tetapi mungkin masih ada jebakan tersembunyi. Salah satu yang terlintas di benak saya adalah bahwa memiliki bit yang dapat dieksekusi pada beberapa file mengubah hash pohon. Anda harus mengeluarkan masalah git config --local core.fileMode falsesebelum melakukan untuk menghindari ini. Saya tidak tahu apakah ada peringatan seperti ini lagi.
Zoltan

14

Anda dapat melakukan tar -c /path/to/folder | sha1sum


17
Jika Anda ingin mereplikasi checksum itu di mesin lain, tar mungkin bukan pilihan yang baik, karena formatnya tampaknya memiliki ruang untuk ambiguitas dan ada di banyak versi, jadi tar di komputer lain mungkin menghasilkan keluaran yang berbeda dari file yang sama.
anjing lambat

2
kekhawatiran berlaku slowdog ini meskipun, jika Anda peduli tentang isi file, perizinan, dll tapi tidak waktu modifikasi, Anda dapat menambahkan --mtimeopsi seperti: tar -c /path/to/folder --mtime="1970-01-01" | sha1sum.
Biner Phile

@ S. Lot jika ukuran direktori besar, maksud saya jika ukuran direktori sangat besar, zip dan mendapatkan md5 di atasnya akan membutuhkan lebih banyak waktu
Kasun Siyambalapitiya

13

Jika Anda hanya ingin memeriksa apakah ada yang berubah di folder, saya merekomendasikan yang ini:

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

Ini hanya akan memberi Anda hash dari output ls, yang berisi folder, sub-folder, file mereka, stempel waktu, ukuran dan izinnya. Hampir semua yang Anda perlukan untuk menentukan apakah sesuatu telah berubah.

Harap dicatat bahwa perintah ini tidak akan menghasilkan hash untuk setiap file, tetapi itulah mengapa harus lebih cepat daripada menggunakan find.


1
Saya tidak yakin mengapa ini tidak memiliki lebih banyak suara positif mengingat kesederhanaan solusinya. Adakah yang bisa menjelaskan mengapa ini tidak bekerja dengan baik?
Dave C

1
Saya kira ini tidak ideal karena hash yang dihasilkan akan didasarkan pada pemilik file, pengaturan format tanggal, dll.
Ryota

1
Perintah ls dapat disesuaikan untuk menghasilkan apa pun yang Anda inginkan. Anda dapat mengganti -l dengan -gG untuk menghilangkan grup dan pemiliknya. Dan Anda dapat mengubah format tanggal dengan opsi --time-style. Pada dasarnya periksa halaman manual ls dan lihat apa yang sesuai dengan kebutuhan Anda.
Shumoapp

@DaveC Karena itu sangat tidak berguna. Jika Anda ingin membandingkan nama file, bandingkan saja secara langsung. Mereka tidak sebesar itu.
Navin

7
@Navin Dari pertanyaan, tidak jelas apakah konten file hash perlu atau mendeteksi perubahan dalam pohon. Setiap kasing memiliki kegunaannya. Menyimpan nama file 45K dalam pohon kernel, misalnya, kurang praktis dibandingkan dengan satu hash. ls -lAgGR --ukuran-blok = 1 - gaya-waktu = +% s | sha1sum bekerja sangat baik untuk saya
yashma

5

Pendekatan yang kuat dan bersih

  • Hal pertama yang pertama, jangan memonopoli memori yang tersedia ! Hash file dalam potongan daripada memberi makan seluruh file.
  • Pendekatan yang berbeda untuk kebutuhan / tujuan yang berbeda (semua di bawah ini atau pilih apa yang pernah berlaku):
    • Hash hanya nama entri dari semua entri dalam pohon direktori
    • Hash konten file dari semua entri (meninggalkan meta seperti, nomor inode, ctime, atime, mtime, ukuran, dll., Anda mendapatkan idenya)
    • Untuk tautan simbolik, isinya adalah nama rujukan. Hash atau pilih untuk melewati
    • Ikuti atau tidak untuk mengikuti (nama terselesaikan) symlink saat melakukan hashing pada konten entri
    • Jika ini sebuah direktori, isinya hanyalah entri direktori. Saat melakukan traverse secara rekursif, pada akhirnya mereka akan di-hash, tetapi haruskah nama entri direktori dari level tersebut di-hash untuk menandai direktori ini? Bermanfaat dalam kasus penggunaan di mana hash diperlukan untuk mengidentifikasi perubahan dengan cepat tanpa harus menjelajahi konten secara mendalam. Contohnya adalah perubahan nama file tetapi konten lainnya tetap sama dan semuanya adalah file yang cukup besar
    • Tangani file besar dengan baik (sekali lagi, perhatikan RAM)
    • Tangani pohon direktori yang sangat dalam (perhatikan deskriptor file yang terbuka)
    • Tangani nama file non standar
    • Bagaimana cara melanjutkan dengan file yang berupa soket, pipa / FIFO, perangkat blok, perangkat char? Haruskah mereka hash juga?
    • Jangan perbarui waktu akses entri mana pun saat melakukan traverse karena ini akan menjadi efek samping dan kontra-produktif (intuitif?) Untuk kasus penggunaan tertentu.

Inilah yang saya pikirkan, siapa pun yang telah menghabiskan beberapa waktu untuk mengerjakan ini secara praktis akan menangkap kasus gotcha dan pojok lainnya.

Ini adalah alat , sangat ringan pada memori, yang menangani sebagian besar kasus, mungkin agak kasar di tepinya tetapi telah cukup membantu.

Contoh penggunaan dan keluaran 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

Cuplikan dari keluaran yang ramah manusia:

...
... //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
Dapatkah Anda memberikan contoh singkat untuk mendapatkan folder yang kuat dan bersih, mungkin untuk folder Windows dengan tiga subdirektori dan beberapa file di dalamnya masing-masing?
Ferit

3

Jika Anda hanya ingin mencirikan konten file, mengabaikan nama file maka Anda dapat menggunakan

cat $FILES | md5sum

Pastikan Anda memiliki file dengan urutan yang sama saat menghitung hash:

cat $(echo $FILES | sort) | md5sum

Tetapi Anda tidak dapat memiliki direktori dalam daftar file Anda.


2
Memindahkan akhir satu file ke awal file yang mengikutinya menurut abjad tidak akan memengaruhi hash tetapi seharusnya. Pembatas file atau panjang file perlu disertakan dalam hash.
Jason Stangroome

3

Alat lain untuk mencapai ini:

http://md5deep.sourceforge.net/

Seperti suara: seperti md5sum tetapi juga rekursif, ditambah fitur lainnya.


1
Meskipun tautan ini mungkin menjawab pertanyaan, lebih baik menyertakan bagian penting dari jawaban di sini dan menyediakan tautan untuk referensi. Jawaban link saja bisa menjadi tidak valid jika halaman tertaut berubah.
Mamoun Benghezal

3

Jika ini adalah repo git dan Anda ingin mengabaikan file apa pun .gitignore, Anda mungkin ingin menggunakan ini:

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

Ini bekerja dengan baik untuk saya.


Terima kasih banyak! :)
visortelle

Untuk banyak aplikasi, pendekatan ini lebih unggul. Hanya hash file kode sumber mendapatkan hash yang cukup unik dalam waktu yang jauh lebih singkat.
John McGehee


2

Saya harus memeriksa seluruh direktori untuk perubahan file.

Tetapi dengan mengecualikan, cap waktu, kepemilikan direktori.

Tujuannya adalah untuk mendapatkan jumlah yang identik di mana saja, jika file tersebut identik.

Termasuk dihosting ke mesin lain, apa pun kecuali file, atau perubahan ke dalamnya.

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

Ini menghasilkan daftar hash berdasarkan file, kemudian menggabungkan hash tersebut menjadi satu.

Ini jauh lebih cepat daripada metode tar.

Untuk privasi yang lebih kuat di hash kami, kami dapat menggunakan sha512sum pada resep yang sama.

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

Hash juga identik di mana saja menggunakan sha512sum tetapi tidak ada cara yang diketahui untuk membalikkannya.


Ini tampaknya jauh lebih sederhana daripada jawaban yang diterima untuk melakukan hashing direktori. Saya tidak menemukan jawaban yang diterima dapat diandalkan. Satu masalah ... apakah ada kemungkinan hash keluar dalam urutan yang berbeda? sha256sum /tmp/thd-agent/* | sortadalah apa yang saya coba untuk pemesanan yang andal, lalu hashing saja.
pikirkan

Hai, sepertinya hash muncul dalam urutan abjad secara default. Apa yang Anda maksud dengan pemesanan yang andal? Anda harus mengatur semua itu sendiri. Misalnya menggunakan array asosiatif, entri + hash. Kemudian Anda mengurutkan array ini berdasarkan entri, ini memberikan daftar hash yang dihitung dalam urutan pengurutan. Saya yakin Anda dapat menggunakan objek json sebaliknya, dan hash seluruh objek secara langsung.
NVRM

Jika saya mengerti Anda mengatakan itu mencirikan file dalam urutan abjad. Sepertinya benar. Sesuatu dalam jawaban yang diterima di atas terkadang memberi saya perintah berbeda yang terputus-putus, jadi saya hanya mencoba memastikan itu tidak terjadi lagi. Saya akan tetap menggunakan penyortiran di bagian akhir. Sepertinya bekerja. Satu-satunya masalah dengan metode ini vs jawaban yang diterima yang saya lihat adalah itu tidak berurusan dengan folder bersarang. Dalam kasus saya, saya tidak memiliki folder apa pun jadi ini berfungsi dengan baik.
pikirtt

tentang apa ls -r | sha256sum?
NVRM

@NVRM mencobanya dan hanya memeriksa perubahan nama file, bukan konten file
Gi0rgi0s

1

Cobalah membuatnya dalam dua langkah:

  1. buat file dengan hash untuk semua file di folder
  2. hash file ini

Seperti:

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

Atau lakukan semuanya sekaligus:

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

for F in 'find ...' ...tidak berfungsi jika Anda memiliki spasi dalam nama (yang selalu Anda lakukan saat ini).
mivk

1

Saya akan menyalurkan hasil untuk file individual melalui sort(untuk mencegah pengubahan urutan file hanya untuk mengubah hash) menjadi md5sumatau sha1sum, mana pun yang Anda pilih.


1

Saya telah menulis skrip Groovy untuk melakukan ini:

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)}"

Anda dapat menyesuaikan penggunaan untuk menghindari pencetakan setiap file, mengubah intisari pesan, mengambil hashing direktori, dll. Saya telah mengujinya terhadap data uji NIST dan berfungsi seperti yang diharapkan. 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

Anda dapat sha1summembuat daftar nilai hash dan kemudian sha1sumdaftar itu lagi, itu tergantung pada apa yang sebenarnya ingin Anda capai.


0

Berikut adalah varian singkat dan sederhana di Python 3 yang berfungsi dengan baik untuk file berukuran kecil (misalnya pohon sumber atau sesuatu, di mana setiap file dapat masuk ke dalam RAM dengan mudah), mengabaikan direktori kosong, berdasarkan ide dari solusi lain:

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()                          

Ini bekerja seperti ini:

  1. Temukan semua file dalam direktori secara rekursif dan urutkan berdasarkan nama
  2. Hitung hash (default: SHA-1) dari setiap file (membaca seluruh file ke dalam memori)
  3. Buat indeks tekstual dengan baris "filename = hash"
  4. Enkode indeks itu kembali menjadi string UTF-8 byte dan hash itu

Anda dapat memasukkan fungsi hash yang berbeda sebagai parameter kedua jika SHA-1 bukan secangkir teh Anda.


0

Sejauh ini cara tercepat untuk melakukannya masih dengan tar. Dan dengan beberapa parameter tambahan kita juga dapat menghilangkan perbedaan yang disebabkan oleh metadata.

Untuk menggunakan tar untuk hash dir, perlu dipastikan Anda mengurutkan path selama tar, jika tidak maka akan selalu berbeda.

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

abaikan waktu

Jika Anda tidak peduli dengan waktu akses atau mengubah waktu juga gunakan sesuatu seperti --mtime='UTC 2019-01-01' untuk memastikan semua cap waktu sama.

abaikan kepemilikan

Biasanya kita perlu menambahkan --group=0 --owner=0 --numeric-owneruntuk menyatukan metadata pemilik.

abaikan beberapa file

menggunakan --exclude=PATTERN

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.