Cara yang tepat tergantung pada mengapa Anda bertanya:
Opsi 1: Bandingkan Hanya Data
Jika Anda hanya memerlukan hash dari isi file tree, ini akan melakukan trik:
$ find -s somedir -type f -exec md5sum {} \; | md5sum
Ini pertama meringkas semua konten file secara individual, dalam urutan yang dapat diprediksi, kemudian melewati daftar nama file dan hash MD5 untuk di-hash sendiri, memberikan nilai tunggal yang hanya berubah ketika konten salah satu file di pohon berubah.
Sayangnya, find -s
hanya berfungsi dengan BSD find (1), digunakan di macOS, FreeBSD, NetBSD dan OpenBSD. Untuk mendapatkan sesuatu yang sebanding pada sistem dengan GNU atau SUS find (1), Anda perlu sesuatu yang sedikit lebih jelek:
$ find somedir -type f -exec md5sum {} \; | sort -k 2 | md5sum
Kami telah mengganti find -s
dengan panggilan ke sort
. The -k 2
bit mengatakan itu untuk melewatkan hash MD5, sehingga hanya mengurutkan nama file, yang di lapangan 2 sampai akhir-of-line, dengan sort
's hisab.
Ada kelemahan dengan versi perintah ini, yaitu kemungkinan besar menjadi bingung jika Anda memiliki nama file dengan baris baru di dalamnya, karena akan terlihat seperti beberapa baris sort
panggilan. The find -s
varian tidak memiliki masalah itu, karena traversal pohon dan penyortiran terjadi dalam program yang sama, find
.
Dalam kedua kasus tersebut, penyortiran diperlukan untuk menghindari kesalahan positif: sistem file Unix / Linux yang paling umum tidak mempertahankan daftar direktori dalam urutan yang stabil dan dapat diprediksi. Anda mungkin tidak menyadari ini dari penggunaan ls
dan semacamnya, yang secara diam-diam mengurutkan isi direktori untuk Anda. find
tanpa -s
atau sort
panggilan akan mencetak file dalam urutan apa pun yang mengembalikan sistem file yang mendasarinya, yang akan menyebabkan perintah ini untuk memberikan nilai hash yang diubah jika urutan file yang diberikan sebagai perubahan input.
Anda mungkin perlu mengubah md5sum
perintah ke md5
atau fungsi hash lainnya. Jika Anda memilih fungsi hash lain dan memerlukan bentuk kedua dari perintah untuk sistem Anda, Anda mungkin perlu menyesuaikan sort
perintah tersebut. Perangkap lain adalah bahwa beberapa program penjumlah data tidak menuliskan nama file sama sekali, contoh utama adalah sum
program Unix yang lama .
Metode ini agak tidak efisien, memanggil md5sum
N + 1 kali, di mana N adalah jumlah file di pohon, tetapi itu adalah biaya yang diperlukan untuk menghindari hashing metadata file dan direktori.
Opsi 2: Bandingkan Data dan Metadata
Jika Anda harus dapat mendeteksi bahwa segala sesuatu dalam pohon telah berubah, bukan hanya konten file, minta tar
untuk mengemas konten direktori untuk Anda, kemudian kirimkan ke md5sum
:
$ tar -cf - somedir | md5sum
Karena tar
juga melihat izin file, kepemilikan, dll., Ini juga akan mendeteksi perubahan pada hal-hal tersebut, bukan hanya perubahan pada konten file.
Metode ini jauh lebih cepat, karena hanya membuat satu melewati pohon dan menjalankan program hash hanya sekali.
Seperti find
metode berbasis di atas, tar
akan memproses nama file dalam urutan yang mengembalikan sistem file yang mendasarinya. Mungkin dalam aplikasi Anda, Anda dapat yakin bahwa ini tidak akan terjadi. Saya bisa memikirkan setidaknya tiga pola penggunaan yang berbeda di mana itu mungkin terjadi. (Saya tidak akan mencantumkannya, karena kita masuk ke wilayah perilaku yang tidak ditentukan. Setiap sistem file dapat berbeda di sini, bahkan dari satu versi OS ke yang berikutnya.)
Jika Anda mendapati diri Anda mendapatkan hasil positif palsu, saya akan merekomendasikan memilih find | cpio
opsi dalam jawaban Gilles .
find .
daripadafind somedir
. Dengan cara ini nama file sama ketika memberikan spesifikasi path yang berbeda untuk ditemukan; ini bisa rumit :-)find somedir -type f -exec sh -c "openssl dgst -sha1 -binary {} | xxd -p" \; | sort | openssl dgst -sha1
untuk mengabaikan semua nama file (harus bekerja dengan baris baru)Checksum harus merupakan representasi deterministik dan tidak ambigu dari file sebagai string. Deterministik berarti bahwa jika Anda meletakkan file yang sama di lokasi yang sama, Anda akan mendapatkan hasil yang sama. Tidak ambigu berarti bahwa dua set file yang berbeda memiliki representasi yang berbeda.
Data dan metadata
Membuat arsip berisi file adalah awal yang baik. Ini adalah representasi yang tidak ambigu (jelas, karena Anda dapat memulihkan file dengan mengekstrak arsip). Ini mungkin termasuk file metadata seperti tanggal dan kepemilikan. Namun, ini belum sepenuhnya benar: arsip bersifat ambigu, karena representasinya tergantung pada urutan penyimpanan file, dan jika berlaku pada kompresi.
Solusinya adalah dengan mengurutkan nama file sebelum mengarsipkannya. Jika nama file Anda tidak mengandung baris baru, Anda dapat menjalankan
find | sort
daftar itu, dan menambahkannya ke arsip dalam urutan ini. Berhati-hatilah untuk memberi tahu pengarsip agar tidak kembali ke direktori. Berikut adalah contoh dengan POSIXpax
, GNU tar dan cpio:Hanya nama dan isinya, cara berteknologi rendah
Jika Anda hanya ingin mengambil data file ke dalam akun dan bukan metadata, Anda dapat membuat arsip yang hanya mencakup konten file, tetapi tidak ada alat standar untuk itu. Alih-alih memasukkan konten file, Anda dapat memasukkan hash file. Jika nama file tidak mengandung baris baru, dan hanya ada file dan direktori biasa (tidak ada tautan simbolik atau file khusus), ini cukup mudah, tetapi Anda perlu mengurus beberapa hal:
Kami menyertakan daftar direktori selain daftar checksum, karena jika tidak, direktori kosong tidak akan terlihat. Daftar file diurutkan (di tempat yang spesifik dan dapat direproduksi - terima kasih kepada Peter.O untuk mengingatkan saya akan hal itu).
echo
memisahkan kedua bagian (tanpa ini, Anda bisa membuat beberapa direktori kosong yang namanya mirip denganmd5sum
keluaran yang juga bisa digunakan untuk file biasa). Kami juga menyertakan daftar ukuran file, untuk menghindari serangan ekstensi-panjang .Omong-omong, MD5 sudah usang. Jika tersedia, pertimbangkan untuk menggunakan SHA-2, atau setidaknya SHA-1.
Nama dan data, mendukung baris baru dalam nama
Berikut adalah varian dari kode di atas yang bergantung pada alat GNU untuk memisahkan nama file dengan null byte. Ini memungkinkan nama file mengandung baris baru. Utilitas digest GNU mengutip karakter khusus dalam output mereka, sehingga tidak akan ada baris baru yang ambigu.
Pendekatan yang lebih kuat
Berikut ini adalah skrip Python yang teruji minimal yang membangun hash yang menggambarkan hierarki file. Dibutuhkan direktori dan isi file ke dalam akun dan mengabaikan tautan simbolik dan file lainnya, dan mengembalikan kesalahan fatal jika file apa pun tidak dapat dibaca.
sumber
LC_ALL=C sort
mengecek dari lingkungan yang berbeda ... (+ 1 btw)LC_ALL=C
sangat penting jika dijalankan pada banyak mesin dan OS.cpio -o -
artinya Bukankah cpio menggunakan stdin / out secara default? GNU cpio 2.12 menghasilkancpio: Too many arguments
Lihatlah md5deep . Beberapa fitur md5deep yang mungkin menarik bagi Anda:
sumber
.../foo: Is a directory
, apa yang menyebabkannya?md5deep -r -l -j0 . | md5sum
(di mana-r
bersifat rekursif,-l
berarti "menggunakan jalur relatif" sehingga jalur absolut dari file tidak mengganggu ketika mencoba membandingkan konten dari dua direktori, dan-j0
berarti menggunakan 1 utas untuk mencegah non-determinisme karena untuk masing-masing md5sums dikembalikan dalam urutan berbeda).Jika tujuan Anda hanya untuk menemukan perbedaan antara dua direktori, pertimbangkan untuk menggunakan diff.
Coba ini:
sumber
Anda dapat hash setiap file secara rekursif dan kemudian hash teks yang dihasilkan:
md5deep diperlukan.
sumber
md5deep
digunakanhashdeep
di ubuntu 16.04 karena paket md5deep hanyalah dummy transisi untuk hashdeep.## Invoked from: /home/myuser/dev/
yang merupakan jalur Anda saat ini dan## $ hashdeep -s -r -l ~/folder/
. Ini harus disortir, jadi hash terakhir akan berbeda jika Anda mengubah folder atau baris perintah saat ini.Isi file hanya , tidak termasuk nama file
Saya membutuhkan versi yang hanya memeriksa nama file karena isinya berada di direktori yang berbeda.
Versi ini (jawaban Warren Young) banyak membantu, tetapi versi saya
md5sum
menampilkan nama file (relatif terhadap path tempat saya menjalankan perintah), dan nama folder berbeda, oleh karena itu meskipun masing-masing file checksum cocok, checksum akhir tidak 't.Untuk memperbaikinya, dalam kasus saya, saya hanya perlu menghapus nama file dari setiap baris
find
output (pilih hanya kata pertama yang dipisahkan oleh spasi menggunakancut
):sumber
solusi :
bekerja dengan cepat dan solusi yang lebih mudah daripada bash scripting.
lihat dokumen: https://pypi.python.org/pypi/checksumdir/1.0.5
sumber
nix-hash
dari manajer paket Nixsumber
Saya menggunakan cuplikan ini untuk volume sedang :
find . -xdev -type f -print0 | LC_COLLATE=C sort -z | xargs -0 cat | md5sum -
dan yang ini untuk XXXL :
find . -xdev -type f -print0 | LC_COLLATE=C sort -z | xargs -0 tail -qc100 | md5sum -
sumber
-xdev
bendera?man find
dan membaca manual yang bagus;)-xdev Don't descend directories on other filesystems.
Pohon check-sum yang baik adalah id pohon Git.
Sayangnya tidak ada alat yang berdiri sendiri yang dapat melakukan itu (setidaknya saya tidak mengetahuinya), tetapi jika Anda memiliki Git, Anda dapat berpura-pura membuat repositori baru dan menambahkan file yang ingin Anda periksa ke indeks.
Ini memungkinkan Anda untuk menghasilkan hash (reproduksi) pohon - yang hanya mencakup konten, nama file, dan beberapa mode file yang diperkecil (dapat dieksekusi).
sumber
Sebagai tindak lanjut dari jawaban yang sangat bagus ini , jika Anda ingin mempercepat perhitungan checksum untuk direktori besar, coba GNU Parallel :
(Ini menggunakan Mac dengan
md5
, ganti sesuai kebutuhan.)The
-k
bendera penting, yang menginstruksikanparallel
untuk menjaga ketertiban, jika jumlah keseluruhan dapat berubah dijalankan untuk menjalankan bahkan jika file-file tersebut semua sama.-n 100
mengatakan untuk menjalankan setiap instancemd5
dengan 100 argumen, ini adalah parameter yang dapat Anda atur untuk run time terbaik. Lihat juga-X
benderaparallel
(meskipun dalam kasus pribadi saya yang menyebabkan kesalahan.)sumber
Sebuah skrip yang diuji dengan baik dan mendukung sejumlah operasi termasuk menemukan duplikat, melakukan perbandingan data dan metadata, menunjukkan penambahan serta perubahan dan pemindahan, Anda mungkin menyukai Sidik Jari .
Sidik jari saat ini tidak menghasilkan satu checksum tunggal untuk direktori, tetapi file transkrip yang mencakup checksum untuk semua file dalam direktori itu.
Ini akan menghasilkan
index.fingerprint
dalam direktori saat ini yang mencakup checksum, nama file, dan ukuran file. Secara default menggunakan keduanyaMD5
danSHA1.256
.Di masa depan, saya berharap dapat menambahkan dukungan untuk Merkle Trees ke dalam Sidik Jari yang akan memberi Anda satu checksum tingkat atas. Saat ini, Anda perlu menyimpan file itu untuk melakukan verifikasi.
sumber
Saya tidak ingin executable baru atau solusi kikuk jadi inilah pendapat saya:
sumber
Pendekatan yang kuat dan bersih
Ini adalah apa yang saya miliki di atas kepala saya, siapa pun yang telah menghabiskan waktu mengerjakan ini praktis akan menangkap gotchas lainnya dan kasing sudut.
Inilah alat (penafian: Saya kontributor untuk itu) dtreetrawl , sangat ringan pada memori, yang membahas sebagian besar kasus, mungkin agak kasar di tepinya tetapi telah cukup membantu.
Contoh keluaran ramah manusia:
sumber
Lakukan secara individual untuk semua file di setiap direktori.
sumber
Migrasi ke format arsip POSIX memengaruhi checksum berbasis GNU Tar
Jawaban ini dimaksudkan sebagai pembaruan tambahan untuk pendekatan penggunaan output Tar untuk hash isi direktori, seperti yang diusulkan (antara lain) dalam jawaban yang sangat baik dari Warren Young dan Gilles beberapa waktu lalu.
Sejak itu, setidaknya openSUSE (sejak dirilis 12.2) mengubah format GNU Tar default dari "GNU tar 1.13.x format" ke format "POSIX 1003.1-2001 (pax)" (sedikit) unggul " . Juga upstream (di antara pengembang GNU Tar) yang mereka diskusikan untuk melakukan migrasi yang sama, lihat misalnya paragraf terakhir di halaman manual GNU Tar ini :
(Halaman ini juga memberikan ulasan yang bagus tentang berbagai format arsip yang tersedia dengan GNU Tar.)
Dalam kasus kami, di mana kami menampung isi direktori dan hash hasilnya, dan tanpa mengambil tindakan spesifik, perubahan dari GNU ke format POSIX memiliki konsekuensi sebagai berikut:
Meskipun konten direktori identik, checksum yang dihasilkan akan berbeda.
Terlepas dari isi direktori yang identik, checksum yang dihasilkan akan berbeda dari menjalankan untuk menjalankan jika header pax default digunakan.
Yang terakhir datang dari fakta, bahwa format POSIX (pax) termasuk header pax diperpanjang yang ditentukan oleh string format yang secara default ke
%d/PaxHeaders.%p/%f
dalam GNU Tar. Dalam string ini, specifier%p
diganti dengan ID proses dari proses Tar menghasilkan, yang tentu saja berbeda dari menjalankan untuk menjalankan. Lihat bagian manual GNU Tar ini dan khususnya yang ini untuk detailnya.Baru saja, mulai dari 2019-03-28, ada komitmen yang diterima di hulu yang meredakan masalah ini.
Jadi, untuk dapat terus menggunakan GNU Tar dalam use case yang diberikan, saya dapat merekomendasikan opsi alternatif berikut:
Gunakan opsi Tar
--format=gnu
untuk secara eksplisit memberi tahu Tar untuk membuat arsip dalam format "lama". Ini wajib untuk memvalidasi checksum "lama".Gunakan format POSIX yang lebih baru, tetapi secara eksplisit tentukan pax header yang sesuai, misalnya oleh
--pax-option="exthdr.name=%d/PaxHeaders/%f"
. Namun, ini memecah kompatibilitas ke belakang ke checksum "lama".Berikut adalah fragmen kode Bash yang saya gunakan secara teratur untuk menghitung checksum dari isi direktori termasuk metadata:
Di sini,
<paths>
digantikan oleh daftar path yang terpisah dari semua direktori yang ingin saya bahas oleh checksum. Tujuan dari menggunakan C locale, pemisahan null byte dari nama file, dan menggunakan find dan sort untuk mendapatkan sistem file independen dari file dalam arsip sudah cukup dibahas dalam jawaban lain.Tanda kurung di sekitarnya menjaga
LC_ALL
pengaturan lokal dalam subkulit.Selain itu, saya menggunakan ekspresi
! -type s
denganfind
untuk menghindari peringatan dari Tar yang terjadi jika file socket adalah bagian dari isi direktori: GNU Tar tidak mengarsipkan soket. Jika Anda lebih suka diberi tahu tentang soket yang dilewati, tinggalkan ekspresi itu.Saya gunakan
--numeric-owner
dengan Tar, untuk dapat memverifikasi checksum nanti di sistem, di mana tidak semua pemilik file diketahui.The
--atime-preserve
pilihan untuk Tar lebih baik dihilangkan jika salah satu<paths>
kebohongan pada perangkat yang dipasang-hanya membaca. Kalau tidak, Anda akan diperingatkan untuk setiap file tunggal yang stempel waktu aksesnya tidak dapat dipulihkan. Untuk penulisan yang diaktifkan<paths>
, saya menggunakan opsi ini, yah, untuk menjaga stempel waktu akses di direktori hash.Opsi Tar
--no-recursion
, yang sudah digunakan dalam proposal Gilles , mencegah Tar dari turun secara rekursif ke direktori dengan sendirinya, dan sebagai gantinya mengoperasikan file demi file pada apa pun yang didapat darifind
output yang diurutkan .Dan akhirnya, itu tidak benar yang saya gunakan
md5sum
: Saya benar-benar menggunakansha256sum
.sumber
Jika Anda tidak membutuhkan md5, Anda dapat mencoba
sumber