Mehrere Digests gleichzeitig berechnen (md5, sha256)?

24

Unter der Annahme, dass Festplatten-E / A und freier RAM ein Engpass sind (während die CPU-Zeit keine Einschränkung darstellt), gibt es ein Tool, mit dem mehrere Message Digests gleichzeitig berechnet werden können?

Ich interessiere mich besonders für die parallele Berechnung der MD-5- und SHA-256-Digests großer Dateien (Größe in Gigabyte). Ich habe es versucht openssl dgst -sha256 -md5, aber es berechnet nur den Hash mit einem Algorithmus.

Pseudocode für das erwartete Verhalten:

for each block:
    for each algorithm:
        hash_state[algorithm].update(block)
for each algorithm:
    print algorithm, hash_state[algorithm].final_hash()
Lekensteyn
quelle
Sie können nur eine Instanz im Hintergrund starten, dann werden beide Hashes gleichzeitig ausgeführt:for i in file1 file2 …; do sha256 "$i"& md5sum "$i"; done
Marco,
2
@Marco Das Problem bei diesem Ansatz ist, dass ein Befehl möglicherweise schneller ist als der andere, was zu einem Festplatten-Cache führt, der später mit denselben Daten geleert und neu gefüllt wird.
Lekensteyn
1
Wenn Sie sich Sorgen um den Festplatten-Cache machen, können Sie die Datei nur einmal einlesen: for i in file1 file2 …; do tee < "$i" >(sha256sum) | md5sum ; doneDann müssen Sie zusätzlichen Code hinzufügen, um den Dateinamen zu markieren, da er als Standardeingabe an md5sumund gesendet wird sha256sum.
Marco

Antworten:

28

Auschecken pee(" tee standard input to pipes") von moreutils. Dies entspricht im Wesentlichen dem teeBefehl von Marco , ist jedoch etwas einfacher zu tippen.

$ echo foo | pee md5sum sha256sum
d3b07384d113edec49eaa6238ad5ff00  -
b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c  -
$ pee md5sum sha256sum <foo.iso
f109ffd6612e36e0fc1597eda65e9cf0  -
469a38cb785f8d47a0f85f968feff0be1d6f9398e353496ff7aa9055725bc63e  -
Matt Nordhoff
quelle
Netter Befehl! Ich habe dieses sehr nützliche Paket bereits installiert und kannte dieses witzige Hilfsprogramm nicht.
Lekensteyn
1
peeHat die beste Oberfläche, einen Zeitvergleich mit anderen Tools finden Sie in diesem Beitrag, der auch ein Multithread-Python-Tool demonstriert.
Lekensteyn
Leider gibt es moreutilsKonflikte mit GNU parallelmeinem Debian-System ... es ist jedoch gut zu wissen, dass es ein solches Tool gibt.
Liori
@Lekensteyn: Ich bekomme einen Konflikt auf Paketebene (dh aptitudeich kann nicht beide Pakete gleichzeitig haben).
Liori
@liori Schade, dass Debian es so implementiert hat, es könnte sich lohnen, einen Fehler in dieser Sache einzureichen. Unter Arch Linux gibt es einen moreutils-parallelNamen, um den Konflikt zu vermeiden.
Lekensteyn
10

Sie können eine forSchleife verwenden, um die einzelnen Dateien tee zu durchlaufen, und dann in Kombination mit der Prozessersetzung (funktioniert unter anderem in Bash und Zsh) die Pipe-Funktion für verschiedene Prüfsummen verwenden.

Beispiel:

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

Sie können auch mehr als zwei Prüfsummen verwenden:

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

Dies hat den Nachteil, dass die Prüfsummen den Dateinamen nicht kennen, da er als Standardeingabe übergeben wird. Wenn dies nicht akzeptabel ist, müssen Sie die Dateinamen manuell eingeben. Vollständiges Beispiel:

for file in *.mkv; do
  echo "$file"
  tee < "$file" >(sha256sum) >(sha384sum) | md5sum
  echo
done > hashfilelist
Marco
quelle
1
Um die Ausgabe kompatibel mit der machen *sumFamilie von Werkzeugen, könnte dies sed Ausdruck stattdessen verwendet werden: sed "s;-\$;${file//;/\\;};(ersetzt die nachgestellten -durch die Dateinamen, aber sicher , dass der Dateiname richtig übersetzt wird).
Lekensteyn
AFAICS funktioniert nur in zsh. In ksh93 und bash geht die Ausgabe von sha256sum nach md5sum. Sie wollen: { tee < "$file" >(sha256sum >&3) | md5sum; } 3>&1. Informationen zum umgekehrten Problem finden Sie unter unix.stackexchange.com/q/153896/22565 .
Stéphane Chazelas
6

Schade, dass das Dienstprogramm openssl keine Befehle für mehrere Digests akzeptiert. Ich denke, der gleiche Befehl für mehrere Dateien ist ein häufigeres Verwendungsmuster. FWIW, die Version des Dienstprogramms openssl auf meinem System (Mepis 11) enthält nur Befehle für sha und sha1, keine der anderen sha-Varianten. Aber ich habe ein Programm namens sha256sum sowie md5sum.

Hier ist ein einfaches Python-Programm, dual_hash.py, das macht, was Sie wollen. Eine Blockgröße von 64 KB scheint für meinen Computer (Intel Pentium 4 2.00 GHz mit 2 G RAM) und YMMV optimal zu sein. Bei kleinen Dateien entspricht die Geschwindigkeit in etwa der von md5sum und sha256sum nacheinander. Bei größeren Dateien ist dies jedoch erheblich schneller. Beispiel: Auf einer 1967063040-Byte-Datei (ein Festplatten-Image einer SD-Karte mit MP3-Dateien) dauert md5sum + sha256sum ungefähr 1m44.9s, dual_hash.py dauert 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))

Ich nehme an, eine C / C ++ Version dieses Programms wäre ein wenig schneller sein, aber nicht viel, da die meiste Arbeit wird von der hashlib Modul erfolgen, das ist in C geschrieben (oder C ++). Wie Sie bereits erwähnt haben, liegt der Engpass bei großen Dateien in der E / A-Geschwindigkeit.

PM 2Ring
quelle
Für eine Datei mit einer Größe von 2,3 G hatte diese Version eine vergleichbare Geschwindigkeit im Vergleich zu md5sumund sha256sumkombiniert (4,7 s + 14,2 s gegenüber 18,7 s für dieses Python-Skript, Datei im Cache; 33,6 s für den Kaltlauf). 64KiB vs 1MiB haben die Situation nicht verändert. Mit kommentiertem Code wurden 5,1 s für md5 (n = 3) und 14,6 s für sha1 (n = 3) ausgegeben. Getestet auf einem i5-460M mit 8 GB RAM. Ich denke, dass dies durch die Verwendung von mehr Threads weiter verbessert werden könnte.
Lekensteyn
C oder C ++ werden wahrscheinlich keine Rolle spielen, da sowieso ein Großteil der Laufzeit im OpenSSL-Modul verbracht wird (von hashlib verwendet). Mehr Threads verbessern die Geschwindigkeit. Lesen Sie diesen Beitrag über ein Python-Skript mit mehreren Threads .
Lekensteyn
@ PM 2Ring - Nur eine Anmerkung. Nach den print-Anweisungen in Ihrer digests () -Funktion müssen Sie mindestens sha löschen. Ich kann nicht sagen, ob du md5 löschen sollst oder nicht. Ich würde nur "del sha" verwenden. Andernfalls enthält jede Datei nach der ersten einen falschen Hash. Um dies zu beweisen, erstellen Sie ein tmp-Verzeichnis und kopieren Sie eine Datei hinein. Erstellen Sie nun zwei Kopien dieser Datei und führen Sie Ihr Skript aus. Sie erhalten 3 verschiedene Hashes, was nicht das ist, was Sie wollen. Bearbeiten: Ich dachte, die Funktion liest über eine Reihe von Dateien, nicht nur eine einzelne Datei auf einmal ... Ignorieren Sie für diese Verwendung. ;)
Terry Wendt
1
@ TerryWendt Sie hatten mich dort für eine Sekunde besorgt. :) Ja, digestsverarbeitet nur eine einzelne Datei bei jedem Aufruf. Selbst wenn Sie es in einer Schleife aufrufen, werden bei jedem Aufruf neue md5 & sha-Kontexte erstellt. FWIW, Sie können meinen wiederaufnehmbaren SHA-256-Hash genießen .
PM 2Ring
5

Sie könnten immer so etwas wie GNU parallel verwenden :

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

Alternativ können Sie auch einfach eine der beiden im Hintergrund ausführen:

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

Oder speichern Sie die Ausgabe in verschiedenen Dateien und führen Sie mehrere Jobs im Hintergrund aus:

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

Dadurch werden so viele md5sumund sha256sumInstanzen gestartet, wie Sie über Dateien verfügen, und alle werden parallel ausgeführt, und ihre Ausgabe wird unter den entsprechenden Dateinamen gespeichert. Achtung, dies kann sehr schwer werden, wenn Sie viele Dateien haben.

terdon
quelle
1
Siehe den Kommentar zu Marco, meine Sorge ist, dass obwohl der Befehl parallel sein wird, auf die langsame Festplatte für die gleichen Daten zweimal zugegriffen wird.
Lekensteyn
Aber würde die Existenz des Festplattencaches Ihre Sorgen nicht unnötig machen?
Twinkles
2
@Twinkles Um Lekensteyn oben zu zitieren: "Das Problem bei diesem Ansatz ist, dass ein Befehl möglicherweise schneller ist als der andere, was zu einem Festplatten-Cache führt, der später mit denselben Daten geleert und neu gefüllt wird."
Matt Nordhoff
2
@MattNordhoff Eine weitere Sache, die ein intelligenter I / O-Scheduler beachten und optimieren sollte. Man könnte denken: "Wie schwer kann es für einen E / A-Scheduler sein, dieses Szenario zu berücksichtigen?" Bei genügend unterschiedlichen Szenarien, die ein E / A-Scheduler berücksichtigen sollte, wird dies plötzlich zu einem schwierigen Problem. Daher stimme ich zu, dass man nicht davon ausgehen sollte, dass sich das Problem durch Caching beheben lässt.
Kasperd
1
Angenommen, die E / A ist erheblich langsamer als eines der beteiligten Tools, sollten beide Tools aufgrund von E / A auf die gleiche Geschwindigkeit verlangsamt werden. Wenn ein Tool nur wenige Datenblöcke mehr als das andere Tool abruft, kann das andere Tool die Berechnungen unter Verwendung der Daten im Festplatten-Cache schnell nachholen. Das ist Theorie, ich würde gerne einige experimentelle Ergebnisse sehen, die das beweisen ...
Liori
3

Aus Neugier, ob ein Multithread-Python-Skript die Laufzeit verkürzen würde, habe ich dieses digest.pySkript erstellt threading.Thread, das Hashes für mehrere Dateien verwendet threading.Queueund hashlibberechnet.

Die Multithread-Python-Implementierung ist in der Tat etwas schneller als die Verwendung peemit coreutils. Java auf der anderen Seite ist ... meh. Die Ergebnisse sind in dieser Festschreibungsnachricht verfügbar :

Zum Vergleich für eine Datei von 2,3 GiB (min / avg / max / sd sec für 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

Die Hash-Ausgabe ist mit der Ausgabe von coreutils kompatibel. Da die Länge vom Hashing-Algorithmus abhängt, wird sie von diesem Tool nicht gedruckt. Verwendung (zum Vergleich peewurde auch hinzugefügt):

$ ./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
quelle
Ich wollte einen Vergleich vorschlagen pee "openssl sha256" "openssl md5" < file, aber ehrlich gesagt habe ich es einfach versucht und es hat Digest.py nicht übertroffen. Es verengte jedoch die Lücke.
Matt Nordhoff
1

Jacksum ist ein kostenloses und plattformunabhängiges Dienstprogramm zum Berechnen und Überprüfen von Prüfsummen, CRCs und Hashes (Message Digests) sowie Zeitstempeln von Dateien. (Auszug aus der Jacksum-Manpage )

Es unterstützt große Dateien, es kann Dateigrößen von bis zu 8 Exabyte (= 8.000.000.000 Gigabyte) verarbeiten, vorausgesetzt, Ihr Betriebssystem bzw. Ihr Dateisystem unterstützt große Dateien. (Auszug aus http://www.jonelo.de/java/jacksum/ )

Anwendungsbeispiel:

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

Beispielausgabe:

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

Führen Sie unter Ubuntu den Befehl aus apt-get install jacksum, um ihn abzurufen .

Alternativ stehen Quellcodes unter zur Verfügung

pallxk
quelle
Während dies die korrekten Prüfsummen ausgibt, berechnet dieses Java-Programm doppelt so langsam wie coreutils. Siehe diese Commit-Nachricht .
Lekensteyn