¿Calcular simultáneamente resúmenes múltiples (md5, sha256)?

25

Bajo el supuesto de que la E / S de disco y la RAM libre es un cuello de botella (mientras que el tiempo de CPU no es la limitación), ¿existe una herramienta que pueda calcular múltiples resúmenes de mensajes a la vez?

Estoy particularmente interesado en calcular los resúmenes MD-5 y SHA-256 de archivos grandes (tamaño en gigabytes), preferiblemente en paralelo. Lo he intentado openssl dgst -sha256 -md5, pero solo calcula el hash usando un algoritmo.

Pseudocódigo para el comportamiento esperado:

for each block:
    for each algorithm:
        hash_state[algorithm].update(block)
for each algorithm:
    print algorithm, hash_state[algorithm].final_hash()
Lekensteyn
fuente
Usted puede simplemente comenzar un caso en el fondo, entonces ambos valores hash en paralelo:for i in file1 file2 …; do sha256 "$i"& md5sum "$i"; done
Marco
2
@Marco El problema con ese enfoque es que un comando puede ser más rápido que el otro, lo que resulta en un caché de disco que se vacía y se rellena más tarde con los mismos datos.
Lekensteyn el
1
Si le preocupa la memoria caché del disco, puede leer el archivo solo una vez: for i in file1 file2 …; do tee < "$i" >(sha256sum) | md5sum ; doneluego debe agregar un código adicional para marcar el nombre del archivo, ya que se envía como entrada estándar a md5sumy sha256sum.
Marco

Respuestas:

28

Retirar pee(" tee standard input to pipes") de moreutils. Esto es básicamente equivalente al teecomando de Marco , pero un poco más simple de escribir.

$ echo foo | pee md5sum sha256sum
d3b07384d113edec49eaa6238ad5ff00  -
b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c  -
$ pee md5sum sha256sum <foo.iso
f109ffd6612e36e0fc1597eda65e9cf0  -
469a38cb785f8d47a0f85f968feff0be1d6f9398e353496ff7aa9055725bc63e  -
Matt Nordhoff
fuente
Buen comando! Ya tengo este paquete muy útil instalado, no sabía de esta utilidad de nombre gracioso.
Lekensteyn
1
peetiene la mejor interfaz, una comparación de tiempo con otras herramientas se puede encontrar en esta publicación que también muestra una herramienta Python de subprocesos múltiples.
Lekensteyn
Desafortunadamente, moreutilsentra GNU parallelen conflicto con mi sistema Debian ... sin embargo, es bueno saber que existe esa herramienta.
liori
@Lekensteyn: Tengo un conflicto en el nivel del paquete (es decir aptitude, no me deja tener los dos paquetes al mismo tiempo).
liori
@liori Lástima que Debian lo haya implementado de esa manera, podría valer la pena presentar un error al respecto. En Arch Linux hay un moreutils-parallelnombre para evitar el conflicto.
Lekensteyn
10

Puede usar un forbucle para recorrer los archivos individuales y luego usarlo tee combinado con la sustitución de procesos (funciona en Bash y Zsh, entre otros) para canalizar a diferentes summers de verificación.

Ejemplo:

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

También puede usar más de dos summers de verificación:

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

Esto tiene la desventaja de que los summers de verificación no conocen el nombre del archivo, ya que se pasa como entrada estándar. Si eso no es aceptable, debe emitir los nombres de archivo manualmente. Ejemplo completo:

for file in *.mkv; do
  echo "$file"
  tee < "$file" >(sha256sum) >(sha384sum) | md5sum
  echo
done > hashfilelist
Marco
fuente
1
Para hacer que la salida sea compatible con la *sumfamilia de herramientas, esta expresión sed podría usarse en su lugar: sed "s;-\$;${file//;/\\;};(reemplazó el final -por el nombre del archivo, pero asegúrese de que el nombre del archivo se escape correctamente).
Lekensteyn
AFAICS, solo funciona en zsh. En ksh93 y bash, la salida de sha256sum va a md5sum. Usted querrá: { tee < "$file" >(sha256sum >&3) | md5sum; } 3>&1. Consulte unix.stackexchange.com/q/153896/22565 para ver el problema inverso.
Stéphane Chazelas
6

Es una pena que la utilidad openssl no acepte múltiples comandos de resumen; Supongo que ejecutar el mismo comando en varios archivos es un patrón de uso más común. FWIW, la versión de la utilidad openssl en mi sistema (Mepis 11) solo tiene comandos para sha y sha1, no ninguna de las otras variantes de sha. Pero tengo un programa llamado sha256sum, así como md5sum.

Aquí hay un programa simple de Python, dual_hash.py, que hace lo que quieres. Un tamaño de bloque de 64k parece ser óptimo para mi máquina (Intel Pentium 4 2.00GHz con 2G de RAM), YMMV. Para archivos pequeños, su velocidad es aproximadamente la misma que ejecutar md5sum y sha256sum en sucesión. Pero para archivos más grandes es significativamente más rápido. Por ejemplo, en un archivo de bytes de 1967063040 (una imagen de disco de una tarjeta SD llena de archivos mp3), md5sum + sha256sum toma alrededor de 1m44.9s, dual_hash.py toma 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))

Supongo que una versión C / C ++ de este programa sería un poco más rápida, pero no mucho, ya que la mayoría del trabajo lo realiza el módulo hashlib, que está escrito en C (o C ++). Y como notó anteriormente, el cuello de botella para archivos grandes es la velocidad de E / S.

PM 2Ring
fuente
Para un archivo de 2.3G, esta versión tenía una velocidad comparable en comparación md5sumy sha256sumcombinada (4.7s + 14.2s vs 18.7s para este script Python, archivo en caché; 33.6s para la ejecución en frío). 64KiB vs 1MiB no cambió la situación. Con el código comentado, 5.1s se gastaron en md5 (n = 3), 14.6s en sha1 (n = 3). Probado en un i5-460M con 8GB de RAM. Supongo que esto podría mejorarse aún más utilizando más hilos.
Lekensteyn el
C o C ++ probablemente no importará, ya que gran parte del tiempo de ejecución se gasta en el módulo OpenSSL de todos modos (utilizado por hashlib). Más hilos mejoran la velocidad, vea esta publicación sobre un script de Python multihilo .
Lekensteyn
@PM 2Ring: solo una nota. Después de las declaraciones de impresión en su función digests (), debe borrar al menos sha. No puedo decir si deberías borrar md5 o no. Simplemente usaría "del sha". Si no lo hace, cada archivo después del primero tendrá un hash incorrecto. Para probarlo, crea un directorio tmp y copia un archivo en él. Ahora haga 2 copias de ese archivo y ejecute su secuencia de comandos. Obtendrá 3 hashes diferentes, que no es lo que desea. Editar: pensé que la función estaba leyendo sobre un conjunto de archivos, no solo leyendo un solo archivo a la vez ... Desprecio por este uso. ;)
Terry Wendt
1
@TerryWendt Me dejaste preocupado por un segundo. :) Sí, digestssolo procesa un único archivo en cada llamada. Entonces, incluso si lo llamó en un bucle, creará nuevos contextos md5 y sha en cada llamada. FWIW, puedes disfrutar de mi hash SHA-256 reanudable .
PM 2Ring
5

Siempre puedes usar algo como GNU paralelo :

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

Alternativamente, simplemente ejecute uno de los dos en segundo plano:

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

O guarde la salida en diferentes archivos y ejecute varios trabajos en segundo plano:

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

Que pondrá en marcha la mayor cantidad md5sumy sha256sumcasos que tiene archivos y todos ellos se ejecutarán en paralelo, el ahorro de su producción a los nombres de los archivos correspondientes. Sin embargo, con cuidado, esto puede volverse pesado si tiene muchos archivos.

terdon
fuente
1
Vea el comentario a Marco, mi preocupación es que, aunque el comando será paralelo, se accede dos veces al disco lento para obtener los mismos datos.
Lekensteyn el
¿Pero la existencia de la memoria caché de disco no haría innecesarias sus preocupaciones?
Twinkles
2
@Twinkles Para citar a Lekensteyn arriba, "El problema con ese enfoque es que un comando puede ser más rápido que el otro, lo que resulta en un caché de disco que se vacía y se rellena más tarde con los mismos datos".
Matt Nordhoff el
2
@MattNordhoff Otra cosa más que un planificador inteligente de E / S debería notar y optimizar. Uno puede pensar: "¿Qué tan difícil puede ser para un planificador de E / S tener en cuenta este escenario?" Pero con suficientes escenarios diferentes que un planificador de E / S debe tener en cuenta, de repente se convierte en un problema difícil. Así que estoy de acuerdo en que no se debe suponer que el almacenamiento en caché se encargará del problema.
Kasperd
1
Suponiendo que el IO es significativamente más lento que cualquiera de las herramientas involucradas, ambas herramientas deberían reducirse a la misma velocidad debido al IO. Por lo tanto, si una herramienta logra obtener pocos bloques de datos más que la otra, la otra herramienta se pondría al día rápidamente con los cálculos utilizando los datos en el caché del disco. Esa es la teoría, me encantaría ver algunos resultados experimentales que lo prueben ...
liori
3

Por curiosidad sobre si un script Python multihilo reduciría el tiempo de ejecución, creé este digest.pyscript que usa threading.Thread, threading.Queuey hashlibpara calcular los hashes para múltiples archivos.

La implementación de Python multiproceso es de hecho un poco más rápida que la que se usa peecon coreutils. Java por otro lado es ... meh. Los resultados están disponibles en este mensaje de confirmación :

A modo de comparación, para un archivo de 2.3 GiB (min / avg / max / sd secs para n = 10):

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

La salida hash es compatible con la salida producida por coreutils. Como la longitud depende del algoritmo de hash, esta herramienta no lo imprime. Uso (para comparación, peetambién se agregó):

$ ./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
fuente
Iba a sugerir comparar pee "openssl sha256" "openssl md5" < file, pero, sinceramente, lo intenté y no superó digest.py. Sin embargo, redujo la brecha.
Matt Nordhoff
1

Jacksum es una utilidad gratuita e independiente de la plataforma para computar y verificar sumas de verificación, CRC y hashes (resúmenes de mensajes), así como marcas de tiempo de archivos. ( Extraído de la página de manual de jacksum )

Es compatible con archivos grandes, puede procesar archivos de hasta 8 Exabytes (= 8,000,000,000 Gigabytes), presupone su sistema operativo, respectivamente, su sistema de archivos también es compatible con archivos grandes. (Extraído de http://www.jonelo.de/java/jacksum/ )

Ejemplo de uso:

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

Salida de muestra:

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

En ubuntu, ejecute el comando apt-get install jacksumpara obtenerlo.

Alternativamente, los códigos fuente están disponibles en

pallxk
fuente
Si bien esto genera las sumas de verificación correctas, este programa Java calcula el doble de lento que los coreutils. Vea este mensaje de confirmación .
Lekensteyn