Secara simultan menghitung beberapa intisari (md5, sha256)?

25

Dengan asumsi bahwa disk I / O dan RAM bebas adalah hambatan (sementara waktu CPU bukan batasan), apakah ada alat yang dapat menghitung beberapa pesan sekaligus?

Saya khususnya tertarik menghitung MD-5 dan SHA-256 yang mencerna file besar (ukuran dalam gigabyte), lebih disukai secara paralel. Saya sudah mencoba openssl dgst -sha256 -md5, tetapi hanya menghitung hash menggunakan satu algoritma.

Kode semu untuk perilaku yang diharapkan:

for each block:
    for each algorithm:
        hash_state[algorithm].update(block)
for each algorithm:
    print algorithm, hash_state[algorithm].final_hash()
Lekensteyn
sumber
Anda bisa memulai satu contoh di latar belakang, lalu kedua hash berjalan secara paralel:for i in file1 file2 …; do sha256 "$i"& md5sum "$i"; done
Marco
2
@Marco Masalah dengan pendekatan itu adalah bahwa satu perintah mungkin lebih cepat dari yang lain, menghasilkan cache disk yang akan dikosongkan dan diisi ulang nanti dengan data yang sama.
Lekensteyn
1
Jika Anda khawatir tentang cache disk, Anda dapat membaca dalam file sekali saja: for i in file1 file2 …; do tee < "$i" >(sha256sum) | md5sum ; doneKemudian Anda harus menambahkan kode tambahan untuk menandai nama file, karena itu dikirim sebagai input standar ke md5sumdan sha256sum.
Marco

Jawaban:

28

Lihat pee(" tee standard input to pipes") dari moreutils. Ini pada dasarnya setara dengan teeperintah Marco , tetapi sedikit lebih mudah untuk diketik.

$ echo foo | pee md5sum sha256sum
d3b07384d113edec49eaa6238ad5ff00  -
b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c  -
$ pee md5sum sha256sum <foo.iso
f109ffd6612e36e0fc1597eda65e9cf0  -
469a38cb785f8d47a0f85f968feff0be1d6f9398e353496ff7aa9055725bc63e  -
Matt Nordhoff
sumber
Perintah yang bagus! Saya sudah menginstal paket yang sangat berguna ini, tidak tahu utilitas bernama lucu ini.
Lekensteyn
1
peememiliki antarmuka terbaik, perbandingan waktu dengan alat lain dapat ditemukan di posting ini yang juga menunjukkan alat Python multi-threaded.
Lekensteyn
Sayangnya, moreutilskonflik dengan GNU parallelsistem Debian saya ... meskipun, ada baiknya mengetahui ada alat seperti itu.
liori
@Lekensteyn: Saya mendapatkan konflik di tingkat paket (mis. aptitudeJangan biarkan saya memiliki kedua paket pada saat yang sama).
liori
@liori Sayang sekali Debian menerapkannya seperti itu, mungkin layak untuk melaporkan bug tentang ini. Di Arch Linux ada moreutils-parallelnama untuk menghindari konflik.
Lekensteyn
10

Anda dapat menggunakan forloop untuk loop di atas file individual dan kemudian menggunakan tee dikombinasikan dengan substitusi proses (bekerja di antara Bash dan Zsh antara lain) untuk pipa ke checksummers yang berbeda.

Contoh:

for file in *.mkv; do
  tee < "$file" >(sha256sum) | md5sum
done

Anda juga dapat menggunakan lebih dari dua checksummers:

for file in *.mkv; do
  tee < "$file" >(sha256sum) >(sha384sum) | md5sum
done

Ini memiliki kelemahan bahwa checksummers tidak tahu nama file, karena dilewatkan sebagai input standar. Jika itu tidak dapat diterima, Anda harus memancarkan nama file secara manual. Contoh lengkap:

for file in *.mkv; do
  echo "$file"
  tee < "$file" >(sha256sum) >(sha384sum) | md5sum
  echo
done > hashfilelist
Marco
sumber
1
Untuk membuat output yang kompatibel dengan *sumkumpulan alat, ekspresi sed ini dapat digunakan sebagai gantinya: sed "s;-\$;${file//;/\\;};(mengganti trailing -dengan nama file, tetapi memastikan bahwa nama file akan lolos dengan benar).
Lekensteyn
AFAICS, ini hanya berfungsi di zsh. Di ksh93 dan bash, output dari sha256sum pergi ke md5sum. Anda akan ingin: { tee < "$file" >(sha256sum >&3) | md5sum; } 3>&1. Lihat unix.stackexchange.com/q/153896/22565 untuk masalah sebaliknya.
Stéphane Chazelas
6

Sangat disayangkan bahwa utilitas openssl tidak menerima banyak perintah digest; Saya kira melakukan perintah yang sama pada banyak file adalah pola penggunaan yang lebih umum. FWIW, versi utilitas openssl pada sistem saya (Mepis 11) hanya memiliki perintah untuk sha dan sha1, bukan salah satu dari varian sha lainnya. Tapi saya punya program yang disebut sha256sum, serta md5sum.

Berikut adalah program Python sederhana, dual_hash.py, yang melakukan apa yang Anda inginkan. Ukuran blok 64k tampaknya optimal untuk mesin saya (Intel Pentium 4 2.00GHz dengan 2G RAM), YMMV. Untuk file kecil, kecepatannya kira-kira sama dengan menjalankan md5sum dan sha256sum secara berurutan. Tetapi untuk file yang lebih besar, ini jauh lebih cepat. Misalnya, pada file byte 1967063040 (gambar disk kartu SD yang penuh dengan file mp3), md5sum + sha256sum memakan waktu sekitar 1m44.9s, dual_hash.py membutuhkan waktu 1m0.312s.

dual_hash.py

#! /usr/bin/env python

''' Calculate MD5 and SHA-256 digests of a file simultaneously

    Written by PM 2Ring 2014.10.23
'''

import sys
import hashlib

def digests(fname, blocksize):
    md5 = hashlib.md5()
    sha = hashlib.sha256()
    with open(fname, 'rb') as f:
        while True:
            block = f.read(blocksize)
            if not block:
                break
            md5.update(block)
            sha.update(block)

    print("md5: %s" % md5.hexdigest())
    print("sha256: %s" % sha.hexdigest())

def main(*argv):
    blocksize = 1<<16 # 64kB
    if len(argv) < 2:
        print("No filename given!\n")
        print("Calculate md5 and sha-256 message digests of a file.")
        print("Usage:\npython %s filename [blocksize]\n" % sys.argv[0])
        print("Default blocksize=%d" % blocksize)
        return 1

    fname = argv[1]

    if len(argv) > 2:
        blocksize = int(sys.argv[2])

    print("Calculating MD5 and SHA-256 digests of %r using a blocksize of %d" % (fname, blocksize))
    digests(fname, blocksize)

if __name__ == '__main__':
    sys.exit(main(*sys.argv))

Saya kira C / C ++ versi program ini akan menjadi sedikit lebih cepat, tapi tidak banyak, karena sebagian besar pekerjaan yang dilakukan oleh modul hashlib, yang adalah ditulis dalam C (atau C ++). Dan seperti yang Anda sebutkan di atas, hambatan untuk file besar adalah kecepatan IO.

PM 2Ring
sumber
Untuk file 2.3G, versi ini memiliki kecepatan yang sebanding dibandingkan md5sumdan sha256sumdigabungkan (4.7s + 14.2s vs 18.7s untuk skrip Python ini, file dalam cache; 33.6s untuk cold run). 64KiB vs 1MiB tidak mengubah situasi. Dengan kode yang dikomentari, 5.1d dihabiskan untuk md5 (n = 3), 14.6d pada sha1 (n = 3). Diuji pada i5-460M dengan 8GB RAM. Saya kira ini bisa lebih ditingkatkan dengan menggunakan lebih banyak utas.
Lekensteyn
C atau C ++ mungkin tidak akan masalah seberapa banyak runtime dihabiskan dalam modul OpenSSL (digunakan oleh hashlib). Semakin banyak utas yang meningkatkan kecepatan, lihat posting ini tentang skrip Python multi-utas .
Lekensteyn
@PM 2Ring - Hanya sebuah catatan. Setelah pernyataan cetak dalam fungsi digest () Anda, Anda harus menghapus setidaknya sha. Saya tidak bisa mengatakan apakah Anda harus menghapus md5 atau tidak. Saya hanya akan menggunakan "del sha". Jika tidak, setiap file setelah yang pertama akan memiliki hash yang salah. Untuk membuktikannya, buat dir tmp dan salin file ke dalamnya. Sekarang buat 2 salinan file itu, dan jalankan skrip Anda. Anda akan mendapatkan 3 hash berbeda, yang bukan itu yang Anda inginkan. Sunting: Saya pikir fungsinya membaca lebih dari satu set file, tidak hanya membaca satu file pada satu waktu ... Mengabaikan penggunaan ini. ;)
Terry Wendt
1
@ JerryWendt Anda membuat saya khawatir di sana sebentar. :) Ya, digestshanya memproses satu file pada setiap panggilan. Jadi, bahkan jika Anda menyebutnya dalam satu lingkaran itu akan membuat konteks md5 & sha baru pada setiap panggilan. FWIW, Anda dapat menikmati hash SHA-256 yang dapat dilanjutkan .
PM 2Ring
5

Anda selalu bisa menggunakan sesuatu seperti GNU parallel :

echo "/path/to/file" | parallel 'md5sum {} & sha256sum {}'

Atau, jalankan saja salah satu dari keduanya di latar belakang:

md5sum /path/to/file & sha256sum /path/to/file

Atau, simpan output ke file yang berbeda dan jalankan beberapa pekerjaan di latar belakang:

for file in *; do
    md5sum "$file" > "$file".md5 &
    sha256sum "$file" > "$file".sha &
done

Itu akan meluncurkan banyak md5sumdan sha256sumcontoh saat Anda memiliki file dan mereka semua akan berjalan secara paralel, menyimpan output mereka ke nama file yang sesuai. Hati-hati, ini bisa menjadi berat jika Anda memiliki banyak file.

terdon
sumber
1
Lihat komentar untuk Marco, kekhawatiran saya adalah bahwa meskipun perintahnya akan paralel, disk lambat diakses dua kali untuk data yang sama.
Lekensteyn
Tapi bukankah keberadaan cache disk membuat kekhawatiran Anda tidak perlu?
Twinkles
2
@Twinkles Mengutip Lekensteyn di atas, "Masalah dengan pendekatan itu adalah bahwa satu perintah mungkin lebih cepat dari yang lain, menghasilkan cache disk yang dikosongkan dan diisi ulang nanti dengan data yang sama."
Matt Nordhoff
2
@MattNordhoff Namun hal lain yang harus diperhatikan dan dioptimalkan oleh penjadwal I / O cerdas. Orang mungkin berpikir: "Seberapa sulit bagi penjadwal I / O untuk memperhitungkan skenario ini?" Tetapi dengan skenario yang cukup berbeda, penjadwal I / O harus memperhitungkannya, tiba-tiba itu menjadi masalah yang sulit. Jadi saya setuju bahwa orang tidak boleh berasumsi bahwa caching akan menyelesaikan masalah.
kasperd
1
Dengan asumsi IO secara signifikan lebih lambat daripada salah satu alat yang terlibat, kedua alat harus diperlambat dengan kecepatan yang sama karena IO. Oleh karena itu, jika satu alat mengelola untuk mendapatkan beberapa blok data lebih dari yang lain, alat lainnya akan dengan cepat mengejar ketinggalan dengan perhitungan menggunakan data dalam cache disk. Itu teori, saya ingin melihat beberapa hasil percobaan membuktikannya ...
liori
3

Karena penasaran apakah skrip Python multi-ulir akan mengurangi waktu berjalan, saya membuat digest.pyskrip ini yang menggunakan threading.Thread, threading.Queuedan hashlibuntuk menghitung hash untuk beberapa file.

Implementasi Python multi-threaded memang sedikit lebih cepat daripada menggunakan peedengan coreutils. Sebaliknya Java adalah ... meh. Hasilnya tersedia dalam pesan komit ini :

Sebagai perbandingan, untuk file 2,3 GiB (min / avg / maks / sd detik untuk n = 10):

  • pee sha256sum md5sum <file: 16.5 / 16.9 /17.4/.305
  • python3 digest.py -sha256 -md5 <file: 13.7 / 15.0 /18.7/1.77
  • python2 digest.py -sha256 -md5 <file: 13.7 / 15.9 /18.7/1.64
  • jacksum -a sha256 + md5 -F '#CHECKSUM {i} #FILENAME': 32.7 / 37.1 /50/6.91

Output hash kompatibel dengan output yang dihasilkan oleh coreutils. Karena panjangnya tergantung pada algoritma hashing, alat ini tidak mencetaknya. Penggunaan (untuk perbandingan, peejuga ditambahkan):

$ ./digest.py -sha256 -md5 digest.py
c217e5aa3c3f9cfaca0d40b1060f6233297a3a0d2728dd19f1de3b28454975f2  digest.py
b575edf6387888a68c93bf89291f611c  digest.py
$ ./digest.py -sha256 -md5 <digest.py
c217e5aa3c3f9cfaca0d40b1060f6233297a3a0d2728dd19f1de3b28454975f2  -
b575edf6387888a68c93bf89291f611c  -
$ pee sha256sum md5sum <digest.py
c217e5aa3c3f9cfaca0d40b1060f6233297a3a0d2728dd19f1de3b28454975f2  -
b575edf6387888a68c93bf89291f611c  -
Lekensteyn
sumber
Saya akan menyarankan membandingkan pee "openssl sha256" "openssl md5" < file, tetapi, jujur, saya hanya mencobanya, dan itu tidak mengalahkan digest.py. Itu mempersempit jeda.
Matt Nordhoff
1

Jacksum adalah utilitas bebas platform independen untuk menghitung dan memverifikasi checksum, CRC, dan hash (pesan intisari) serta cap waktu file. (disarikan dari halaman manual jacksum )

Ini adalah file yang besar, dapat memproses file hingga 8 Exabytes (= 8.000.000.000 Gigabytes), mengandaikan sistem operasi Anda masing-masing. Sistem file Anda juga file yang besar. (disarikan dari http://www.jonelo.de/java/jacksum/ )

Contoh penggunaan:

jacksum -a md5+sha256 -F "#ALGONAME{i} (#FILENAME) = #CHECKSUM{i}" jacksum-testfile

Output sampel:

md5 (jacksum-testfile) = d41d8cd98f00b204e9800998ecf8427e
sha256 (jacksum-testfile) = e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

Di ubuntu, jalankan perintah apt-get install jacksumuntuk mendapatkannya.

Atau, kode sumber tersedia di

pallxk
sumber
Meskipun ini menghasilkan checksum yang benar, program Java ini menghitung dua kali lebih lambat dari coreutils. Lihat pesan komit ini .
Lekensteyn