Calculer simultanément plusieurs résumés (md5, sha256)?

25

Dans l'hypothèse où les E / S disque et la RAM libre sont un goulot d'étranglement (alors que le temps CPU n'est pas la limitation), existe-t-il un outil qui peut calculer plusieurs résumés de messages à la fois?

Je suis particulièrement intéressé par le calcul des synthèses MD-5 et SHA-256 de gros fichiers (taille en gigaoctets), de préférence en parallèle. J'ai essayé openssl dgst -sha256 -md5, mais il ne calcule le hachage qu'en utilisant un seul algorithme.

Pseudo-code pour le comportement attendu:

for each block:
    for each algorithm:
        hash_state[algorithm].update(block)
for each algorithm:
    print algorithm, hash_state[algorithm].final_hash()
Lekensteyn
la source
Vous pouvez simplement démarrer une instance en arrière-plan, puis les deux hachages s'exécutent en parallèle:for i in file1 file2 …; do sha256 "$i"& md5sum "$i"; done
Marco
2
@Marco Le problème avec cette approche est qu'une commande peut être plus rapide que l'autre, ce qui entraîne un cache disque qui est vidé et rempli plus tard avec les mêmes données.
Lekensteyn
1
Si vous êtes préoccupé par le cache disque, vous pouvez lire le fichier une seule fois: for i in file1 file2 …; do tee < "$i" >(sha256sum) | md5sum ; doneEnsuite, vous devez ajouter du code supplémentaire pour marquer le nom du fichier, car il est envoyé en tant qu'entrée standard vers md5sumet sha256sum.
Marco

Réponses:

28

Consultez pee(" tee standard input to pipes") à partir de moreutils. C'est fondamentalement équivalent à la teecommande de Marco , mais un peu plus simple à taper.

$ echo foo | pee md5sum sha256sum
d3b07384d113edec49eaa6238ad5ff00  -
b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c  -
$ pee md5sum sha256sum <foo.iso
f109ffd6612e36e0fc1597eda65e9cf0  -
469a38cb785f8d47a0f85f968feff0be1d6f9398e353496ff7aa9055725bc63e  -
Matt Nordhoff
la source
Belle commande! J'ai déjà installé ce paquet très utile, je ne connaissais pas cet utilitaire drôle.
Lekensteyn
1
peea la meilleure interface, une comparaison de temps avec d'autres outils peut être trouvée dans cet article qui montre également un outil Python multi-thread.
Lekensteyn
Malheureusement, il y a des moreutilsconflits avec GNU parallelmon système Debian… cependant, il est bon de savoir qu'il existe un tel outil.
liori
@Lekensteyn: J'obtiens un conflit au niveau du package (c'est-à-dire aptitudequ'il ne me permet pas d'avoir les deux packages en même temps).
liori
@liori Dommage que Debian l'ait implémenté de cette façon, cela pourrait valoir la peine de déposer un bug à ce sujet. Sur Arch Linux, il y a un moreutils-parallelnom pour éviter le conflit.
Lekensteyn
10

Vous pouvez utiliser une forboucle pour parcourir les fichiers individuels, puis utiliser une tee combinaison avec la substitution de processus (fonctionne dans Bash et Zsh, entre autres) pour diriger vers différents vérificateurs.

Exemple:

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

Vous pouvez également utiliser plus de deux checksums:

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

Cela présente l'inconvénient que les vérificateurs ne connaissent pas le nom du fichier, car il est transmis en tant qu'entrée standard. Si ce n'est pas acceptable, vous devez émettre les noms de fichiers manuellement. Exemple complet:

for file in *.mkv; do
  echo "$file"
  tee < "$file" >(sha256sum) >(sha384sum) | md5sum
  echo
done > hashfilelist
Marco
la source
1
Pour rendre la sortie compatible avec la *sumfamille d'outils, cette expression sed pourrait être utilisée à la place: sed "s;-\$;${file//;/\\;};(remplacé la fin -par le nom de fichier, mais assurez-vous que le nom de fichier est correctement échappé).
Lekensteyn
AFAICS, ça ne marche qu'en zsh. Dans ksh93 et ​​bash, la sortie de sha256sum passe à md5sum. Vous aurez envie: { tee < "$file" >(sha256sum >&3) | md5sum; } 3>&1. Voir unix.stackexchange.com/q/153896/22565 pour le problème inverse.
Stéphane Chazelas
6

Il est dommage que l'utilitaire openssl n'accepte pas plusieurs commandes de résumé; Je suppose que l'exécution de la même commande sur plusieurs fichiers est un modèle d'utilisation plus courant. FWIW, la version de l'utilitaire openssl sur mon système (Mepis 11) n'a que des commandes pour sha et sha1, pas pour les autres variantes de sha. Mais j'ai un programme appelé sha256sum, ainsi que md5sum.

Voici un simple programme Python, dual_hash.py, qui fait ce que vous voulez. Une taille de bloc de 64 Ko semble être optimale pour ma machine (Intel Pentium 4 2,00 GHz avec 2 Go de RAM), YMMV. Pour les petits fichiers, sa vitesse est à peu près la même que l'exécution successive de md5sum et sha256sum. Mais pour les fichiers plus volumineux, c'est beaucoup plus rapide. Par exemple, sur un fichier d'octets 1967063040 (une image disque d'une carte SD remplie de fichiers mp3), md5sum + sha256sum prend environ 1m44.9s, dual_hash.py prend 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))

Je suppose qu'une version C / C ++ de ce programme serait un peu plus rapide, mais pas beaucoup, car la plupart du travail est effectué par le module hashlib, qui est écrit en C (ou C ++). Et comme vous l'avez noté ci-dessus, le goulot d'étranglement pour les gros fichiers est la vitesse d'E / S.

PM 2Ring
la source
Pour un fichier de 2.3G, cette version a une vitesse comparable par rapport à md5sumet sha256sumcombinée (4.7s + 14.2s vs 18.7s pour ce script Python, fichier en cache; 33.6s pour le run à froid). 64KiB vs 1MiB n'a pas changé la situation. Le code étant commenté, 5,1 s ont été dépensés pour md5 (n = 3), 14,6 s pour sha1 (n = 3). Testé sur un i5-460M avec 8 Go de RAM. Je suppose que cela pourrait encore être amélioré en utilisant plus de threads.
Lekensteyn
C ou C ++ n'aura probablement pas autant d'importance que la majeure partie du temps d'exécution est dépensée dans le module OpenSSL (utilisé par hashlib). Plus de threads améliorent la vitesse, consultez cet article sur un script Python multi-thread .
Lekensteyn
@PM 2Ring - Juste une note. Après les instructions print dans votre fonction digests (), vous devez effacer au moins sha. Je ne peux pas dire si vous devez effacer md5 ou non. Je voudrais juste utiliser "del sha". Si vous ne le faites pas, chaque fichier après le premier aura un hachage incorrect. Pour le prouver, créez un répertoire tmp et copiez-y un fichier. Faites maintenant 2 copies de ce fichier et exécutez votre script. Vous obtiendrez 3 hachages différents, ce qui n'est pas ce que vous voulez. Edit: Je pensais que la fonction lisait un ensemble de fichiers, pas seulement la lecture d'un seul fichier à la fois ... Ne tenez pas compte de cette utilisation. ;)
Terry Wendt
1
@TerryWendt Vous m'avez fait m'inquiéter une seconde. :) Oui, digestsne traite qu'un seul fichier à chaque appel. Donc, même si vous l'avez appelé en boucle, il créera de nouveaux contextes md5 & sha à chaque appel. FWIW, vous pouvez profiter de mon hachage SHA-256 de reprise .
PM 2Ring
5

Vous pouvez toujours utiliser quelque chose comme GNU parallel :

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

Alternativement, exécutez simplement l'un des deux en arrière-plan:

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

Ou, enregistrez la sortie dans différents fichiers et exécutez plusieurs travaux en arrière-plan:

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

Cela va lancer le plus grand nombre md5sumet les sha256suminstances que vous avez des fichiers et ils fonctionnent tous en parallèle, sauver leur sortie aux noms de fichiers correspondants. Attention cependant, cela peut devenir lourd si vous avez de nombreux fichiers.

terdon
la source
1
Voir le commentaire de Marco, mon inquiétude est que bien que la commande soit parallèle, le disque lent est accédé deux fois pour les mêmes données.
Lekensteyn
Mais l'existence du cache disque ne rendrait-elle pas vos soucis inutiles?
Twinkles
2
@Twinkles Pour citer Lekensteyn ci-dessus, "Le problème avec cette approche est qu'une commande peut être plus rapide que l'autre, résultant en un cache disque qui est vidé et rechargé plus tard avec les mêmes données."
Matt Nordhoff
2
@MattNordhoff Encore une autre chose qu'un planificateur d'E / S intelligent devrait remarquer et optimiser. On peut penser: "Dans quelle mesure un planificateur d'E / S peut-il être difficile de prendre en compte ce scénario?" Mais avec suffisamment de scénarios différents qu'un planificateur d'E / S devrait prendre en compte, cela devient soudainement un problème difficile. Je suis donc d'accord pour ne pas supposer que la mise en cache résoudra le problème.
kasperd
1
En supposant que l'IO est beaucoup plus lent que tous les outils impliqués, les deux outils doivent être ralentis à la même vitesse en raison de l'IO. Par conséquent, si un outil parvient à obtenir quelques blocs de données de plus que l'autre, l'autre outil rattraperait rapidement les calculs en utilisant les données du cache disque. C'est de la théorie, j'aimerais voir des résultats expérimentaux le prouver…
liori
3

Sur curiousity si un script Python multi-thread réduirait le temps d' exécution, j'ai créé ce digest.pyscénario qui usages threading.Thread, threading.Queueet hashlibde les calculer pour plusieurs fichiers.

L'implémentation Python multi-thread est en effet légèrement plus rapide que l'utilisation peeavec coreutils. Java d'autre part est ... meh. Les résultats sont disponibles dans ce message de validation :

A titre de comparaison, pour un fichier de 2,3 Gio (min / moy / max / sd secs pour n = 10):

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

La sortie de hachage est compatible avec la sortie produite par coreutils. Étant donné que la longueur dépend de l'algorithme de hachage, cet outil ne l'imprime pas. Utilisation (pour comparaison, peea également été ajoutée):

$ ./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
la source
J'allais suggérer de comparer pee "openssl sha256" "openssl md5" < file, mais, honnêtement, je l'ai juste essayé, et il n'a pas battu digest.py. Cela a cependant réduit l'écart.
Matt Nordhoff
1

Jacksum est un utilitaire gratuit et indépendant de la plate-forme pour calculer et vérifier les sommes de contrôle, les CRC et les hachages (résumés de messages) ainsi que les horodatages des fichiers. (extrait de la page de manuel jacksum )

Il est sensible aux fichiers volumineux, il peut traiter des tailles de fichiers jusqu'à 8 exaoctets (= 8 000 000 000 gigaoctets), présupposant respectivement votre système d'exploitation, votre système de fichiers est également sensible aux fichiers volumineux. (extrait de http://www.jonelo.de/java/jacksum/ )

Exemple d'utilisation:

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

Exemple de sortie:

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

Sur ubuntu, exécutez la commande apt-get install jacksumpour l'obtenir.

Alternativement, les codes source sont disponibles sur

pallxk
la source
Bien que cela génère les sommes de contrôle correctes, ce programme Java calcule deux fois plus lentement que coreutils. Voir ce message de validation .
Lekensteyn