Jednocześnie obliczyć wiele skrótów (MD5, SHA256)?

25

Zakładając, że dyskowe operacje we / wy i wolna pamięć RAM stanowią wąskie gardło (choć czas procesora nie jest ograniczeniem), czy istnieje narzędzie, które może obliczyć wiele skrótów wiadomości jednocześnie?

Szczególnie interesuje mnie obliczanie skrótów MD-5 i SHA-256 dużych plików (rozmiar w gigabajtach), najlepiej równolegle. Próbowałem openssl dgst -sha256 -md5, ale oblicza skrót tylko przy użyciu jednego algorytmu.

Pseudokod oczekiwanego zachowania:

for each block:
    for each algorithm:
        hash_state[algorithm].update(block)
for each algorithm:
    print algorithm, hash_state[algorithm].final_hash()
Lekensteyn
źródło
Możesz po prostu uruchomić jedną instancję w tle, a następnie oba skróty działają równolegle:for i in file1 file2 …; do sha256 "$i"& md5sum "$i"; done
Marco
2
@Marco Problem z tym podejściem polega na tym, że jedno polecenie może być szybsze od drugiego, w wyniku czego pamięć podręczna dysku jest opróżniana i uzupełniana później tymi samymi danymi.
Lekensteyn
1
Jeśli martwisz się pamięcią podręczną dysku, możesz odczytać plik tylko raz: for i in file1 file2 …; do tee < "$i" >(sha256sum) | md5sum ; doneNastępnie musisz dodać dodatkowy kod, aby oznaczyć nazwę pliku, ponieważ jest on wysyłany jako standardowe wejście do md5sumi sha256sum.
Marco

Odpowiedzi:

28

Sprawdź pee(„ tee standard input to pipes”) od moreutils. Jest to w zasadzie równoważne poleceniu Marco tee, ale nieco prostsze w pisaniu.

$ echo foo | pee md5sum sha256sum
d3b07384d113edec49eaa6238ad5ff00  -
b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c  -
$ pee md5sum sha256sum <foo.iso
f109ffd6612e36e0fc1597eda65e9cf0  -
469a38cb785f8d47a0f85f968feff0be1d6f9398e353496ff7aa9055725bc63e  -
Matt Nordhoff
źródło
Niezłe polecenie! Mam już zainstalowany bardzo przydatny pakiet, nie wiedziałem o tym śmiesznie nazwanym narzędziu.
Lekensteyn
1
peema najlepszy interfejs, porównanie czasu z innymi narzędziami można znaleźć w tym poście, który pokazuje również wielowątkowe narzędzie Python.
Lekensteyn
Niestety moreutilskonflikty z GNU parallelmoim systemem Debian… dobrze wiedzieć, że istnieje takie narzędzie.
liori
@ Lekensteyn: Występuje konflikt na poziomie pakietu (tzn. aptitudeNie pozwala mi mieć obu pakietów jednocześnie).
liori
@liori Szkoda, że ​​Debian zaimplementował to w ten sposób, może warto zgłosić błąd. W Arch Linux istnieje moreutils-parallelnazwa pozwalająca uniknąć konfliktu.
Lekensteyn
10

Możesz użyć forpętli do zapętlenia poszczególnych plików, a następnie użyć w tee połączeniu z podstawieniem procesu (działa między innymi w Bash i Zsh), aby przesłać do różnych sum kontrolnych.

Przykład:

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

Możesz także użyć więcej niż dwóch sum kontrolnych:

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

Ma to tę wadę, że sumy kontrolne nie znają nazwy pliku, ponieważ jest on przekazywany jako standardowe wejście. Jeśli to nie jest dopuszczalne, musisz ręcznie wysłać nazwy plików. Kompletny przykład:

for file in *.mkv; do
  echo "$file"
  tee < "$file" >(sha256sum) >(sha384sum) | md5sum
  echo
done > hashfilelist
Marco
źródło
1
Aby dane wyjściowe były zgodne z *sumrodziną narzędzi, można zamiast tego użyć wyrażenia sed: sed "s;-\$;${file//;/\\;};(zastąpiono znak końcowy -nazwą pliku, ale należy się upewnić, że nazwa pliku zostanie poprawnie zmieniona).
Lekensteyn
AFAICS, działa tylko w zsh. W ksh93 i bash wyjście sha256sum przechodzi do md5sum. Będziemy chcieli: { tee < "$file" >(sha256sum >&3) | md5sum; } 3>&1. Zobacz unix.stackexchange.com/q/153896/22565, aby uzyskać informacje o odwrotnym problemie.
Stéphane Chazelas
6

Szkoda, że ​​narzędzie openssl nie akceptuje wielu poleceń skrótu; Wydaje mi się, że wykonywanie tego samego polecenia na wielu plikach jest bardziej powszechnym wzorcem użycia. FWIW, wersja narzędzia openssl w moim systemie (Mepis 11) zawiera tylko polecenia sha i sha1, a nie inne warianty sha. Ale mam program o nazwie sha256sum, a także md5sum.

Oto prosty program w języku Python, dual_hash.py, który robi to, co chcesz. Rozmiar bloku 64k wydaje się być optymalny dla mojego komputera (Intel Pentium 4 2,00 GHz z 2 GB pamięci RAM), YMMV. W przypadku małych plików jego prędkość jest w przybliżeniu taka sama, jak uruchamianie kolejno md5sum i sha256sum. Ale w przypadku większych plików jest to znacznie szybsze. Np. W pliku bajtów 1967063040 (obraz dysku z kartą SD pełną plików mp3), md5sum + sha256sum zajmuje około 1m44,9s, dual_hash.py zajmuje 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))

Przypuszczam, C / C ++ wersja tego programu byłoby trochę szybciej, ale nie za dużo, ponieważ większość prac jest wykonywana przez moduł hashlib, który jest napisany w C (lub C ++). Jak wspomniano powyżej, wąskim gardłem w przypadku dużych plików jest szybkość operacji we / wy.

PM 2 Ring
źródło
W przypadku pliku 2.3G ta wersja miała porównywalną prędkość w porównaniu md5sumi sha256sumpołączoną (4,7 s + 14,2 s vs 18,7 s dla tego skryptu Python, plik w pamięci podręcznej; 33,6 s dla zimnego uruchomienia). 64KiB vs 1MiB nie zmieniło sytuacji. Z komentarzem do kodu 5.1s wydano na md5 (n = 3), a 14,6s na sha1 (n = 3). Testowane na i5-460M z 8 GB pamięci RAM. Myślę, że można to jeszcze poprawić, używając więcej wątków.
Lekensteyn
C i C ++ prawdopodobnie nie będą miały znaczenia, tyle ile czasu wykonania jest w każdym razie spędzane w module OpenSSL (używanym przez hashlib). Więcej wątków poprawia szybkość, zobacz ten post na temat wielowątkowego skryptu Python .
Lekensteyn
@PM 2Ring - Notatka. Po instrukcjach print w funkcji digest () musisz wyczyścić przynajmniej sha. Nie mogę powiedzieć, czy powinieneś wyczyścić md5, czy nie. Użyłbym po prostu „del sha”. Jeśli nie, każdy plik po pierwszym będzie miał niepoprawny skrót. Aby to udowodnić, stwórz katalog tmp i skopiuj do niego plik. Teraz zrób 2 kopie tego pliku i uruchom skrypt. Otrzymasz 3 różne wartości skrótu, co nie jest tym, czego chcesz. Edycja: Myślałem, że funkcja odczytuje zestaw plików, a nie tylko odczytuje pojedynczy plik naraz ... Zignoruj ​​to użycie. ;)
Terry Wendt
1
@TerryWendt Przez chwilę mnie martwiłeś. :) Tak, digestsprzetwarza tylko jeden plik dla każdego połączenia. Więc nawet jeśli wywołałeś to w pętli, utworzy nowe konteksty md5 i sha przy każdym wywołaniu. FWIW, możesz cieszyć się moim wznawiającym się skrótem SHA-256 .
PM 2, pierścień
5

Zawsze możesz użyć czegoś takiego jak GNU równolegle :

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

Alternatywnie, po prostu uruchom jeden z dwóch w tle:

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

Lub zapisz dane wyjściowe w różnych plikach i uruchom wiele zadań w tle:

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

Spowoduje to uruchomienie dowolnej liczby instancji md5sumi ich liczby, a sha256sumwszystkie będą działać równolegle, zapisując dane wyjściowe pod odpowiednimi nazwami plików. Ostrożnie, może to być ciężkie, jeśli masz wiele plików.

terdon
źródło
1
Zobacz komentarz do Marco, martwię się, że chociaż polecenie będzie równoległe, dostęp do wolnego dysku będzie dwukrotnie uzyskiwany dla tych samych danych.
Lekensteyn
Ale czy istnienie pamięci podręcznej dysku nie sprawi, że twoje zmartwienia nie będą konieczne?
Twinkles,
2
@Twinkles Aby zacytować Lekensteyn powyżej, „Problem z tym podejściem polega na tym, że jedno polecenie może być szybsze od drugiego, w wyniku czego pamięć podręczna dysku jest opróżniana i uzupełniana później tymi samymi danymi”.
Matt Nordhoff,
2
@MattNordhoff Jeszcze jedna rzecz, na którą inteligentny planista we / wy powinien zwrócić uwagę i zoptymalizować. Ktoś może pomyśleć: „Jak trudne może być dla harmonogramu we / wy uwzględnienie tego scenariusza?” Ale przy wystarczającej liczbie różnych scenariuszy, które powinien wziąć pod uwagę harmonogram we / wy, nagle staje się to trudnym problemem. Zgadzam się więc, że nie należy zakładać, że problem rozwiąże buforowanie.
kasperd
1
Zakładając, że IO jest znacznie wolniejsze niż którekolwiek z zaangażowanych narzędzi, oba narzędzia powinny zostać spowolnione do tej samej prędkości z powodu IO. Dlatego jeśli jedno narzędzie zdoła uzyskać więcej bloków danych więcej niż drugie, drugie narzędzie szybko dogoni obliczenia przy użyciu danych z pamięci podręcznej dysku. To teoria, chciałbym zobaczyć wyniki eksperymentów, które to potwierdzają…
liori
3

Z ciekawości czy wielowątkowy scenariusz Python by ograniczyć czas pracy, stworzyłem ten digest.pyskrypt, który używa threading.Thread, threading.Queuea hashlibdo obliczenia wartości mieszania dla wielu plików.

Wielowątkowa implementacja Pythona jest rzeczywiście nieco szybsza niż w peeprzypadku coreutils. Z drugiej strony Java to ... meh. Wyniki są dostępne w tym komunikacie zatwierdzenia :

Dla porównania, dla pliku 2,3 ​​GiB (min / avg / max / sd s dla n = 10):

  • siusiu sha256sum md5sum <plik: 16,5 / 16,9 /17.4/.305
  • python3 digest.py -sha256 -md5 <plik: 13,7 / 15,0 / 18,7 / 1.77
  • python2 digest.py -sha256 -md5 <plik: 13,7 / 15,9 / 18,7 / 1.64
  • jacksum -a sha256 + md5 -F '#CHECKSUM {i} #FILENAME': 32.7 / 37,1 6,91

Dane wyjściowe mieszania są zgodne z danymi wyjściowymi produkowanymi przez coreutils. Ponieważ długość zależy od algorytmu mieszania, narzędzie to nie drukuje. Zastosowanie (dla porównania peerównież zostało dodane):

$ ./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
źródło
Chciałem zasugerować porównanie pee "openssl sha256" "openssl md5" < file, ale, szczerze mówiąc, właśnie tego spróbowałem i nie pobiło ono straw.py. Jednak zmniejszył lukę.
Matt Nordhoff
1

Jacksum to bezpłatne i niezależne od platformy narzędzie do obliczania i weryfikacji sum kontrolnych, CRC i skrótów (skrótów wiadomości), a także znaczników czasu plików. (fragment strony podręcznika użytkownika jacksum )

Jest świadomy dużych plików, może przetwarzać pliki o wielkości do 8 eksabajtów (= 8 000 000 000 gigabajtów), zakładając, że twój system operacyjny lub twój system plików też jest świadomy dużych plików. (fragment z http://www.jonelo.de/java/jacksum/ )

Przykład użycia:

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

Przykładowe dane wyjściowe:

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

Na Ubuntu, uruchom polecenie, apt-get install jacksumaby go zdobyć.

Alternatywnie, kody źródłowe są dostępne pod adresem

pallxk
źródło
Chociaż generuje to prawidłowe sumy kontrolne, ten program Java oblicza dwa razy wolniej niż coreutils. Zobacz ten komunikat zatwierdzenia .
Lekensteyn