Calcular simultaneamente vários resumos (md5, sha256)?

25

Sob a suposição de que a E / S do disco e a RAM livre são um gargalo (embora o tempo da CPU não seja a limitação), existe uma ferramenta que pode calcular vários resumos de mensagens de uma só vez?

Estou particularmente interessado em calcular os resumos MD-5 e SHA-256 de arquivos grandes (tamanho em gigabytes), de preferência em paralelo. Eu tentei openssl dgst -sha256 -md5, mas ele só calcula o hash usando um algoritmo.

Pseudo-código para o comportamento esperado:

for each block:
    for each algorithm:
        hash_state[algorithm].update(block)
for each algorithm:
    print algorithm, hash_state[algorithm].final_hash()
Lekensteyn
fonte
Você pode simplesmente iniciar uma instância em segundo plano e, em seguida, os dois hashes são executados em paralelo:for i in file1 file2 …; do sha256 "$i"& md5sum "$i"; done
Marco
2
@Marco O problema dessa abordagem é que um comando pode ser mais rápido que o outro, resultando em um cache de disco que é esvaziado e reabastecido posteriormente com os mesmos dados.
Lekensteyn
1
Se você estiver preocupado com o cache do disco, poderá ler o arquivo apenas uma vez: for i in file1 file2 …; do tee < "$i" >(sha256sum) | md5sum ; doneDepois, adicione um código adicional para marcar o nome do arquivo, porque ele é enviado como entrada padrão para md5sume sha256sum.
Marco

Respostas:

28

Confira pee(" tee standard input to pipes") em moreutils. Isso é basicamente equivalente ao teecomando de Marco , mas um pouco mais simples de digitar.

$ echo foo | pee md5sum sha256sum
d3b07384d113edec49eaa6238ad5ff00  -
b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c  -
$ pee md5sum sha256sum <foo.iso
f109ffd6612e36e0fc1597eda65e9cf0  -
469a38cb785f8d47a0f85f968feff0be1d6f9398e353496ff7aa9055725bc63e  -
Matt Nordhoff
fonte
Bom comando! Eu já tenho esse pacote muito útil instalado, não conhecia esse utilitário de nome engraçado.
Lekensteyn
1
peetem a melhor interface, uma comparação de tempo com outras ferramentas pode ser encontrada neste post, que também demonstra uma ferramenta Python multiencadeada.
Lekensteyn
Infelizmente, há moreutilsconflitos GNU parallelno meu sistema Debian ... é bom saber que existe uma ferramenta desse tipo.
liori 24/10
@Lekensteyn: Eu tenho um conflito no nível do pacote (ou seja aptitude, não me deixa ter os dois pacotes ao mesmo tempo).
liori 24/10
@liori Pena que o Debian tenha implementado dessa maneira, pode valer a pena registrar um bug sobre isso. No Arch Linux, há um moreutils-parallelnome para evitar o conflito.
Lekensteyn
10

Você pode usar um forloop para percorrer os arquivos individuais e, em seguida, usar tee combinado com a substituição do processo (funciona no Bash e no Zsh entre outros) para canalizar para diferentes verões de verificação.

Exemplo:

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

Você também pode usar mais de dois verões de verificação:

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

Isso tem a desvantagem de que os verificadores de verificação não sabem o nome do arquivo, porque é passado como entrada padrão. Se isso não for aceitável, você deverá emitir os nomes dos arquivos manualmente. Exemplo completo:

for file in *.mkv; do
  echo "$file"
  tee < "$file" >(sha256sum) >(sha384sum) | md5sum
  echo
done > hashfilelist
Marco
fonte
1
Para tornar a saída compatível com a *sumfamília de ferramentas, essa expressão sed poderia ser usada: sed "s;-\$;${file//;/\\;};(substituiu a trilha -pelo nome do arquivo, mas assegure-se de que o nome do arquivo seja escapado adequadamente).
Lekensteyn
AFAICS, ele só funciona zsh. No ksh93 e no bash, a saída do sha256sum vai para o md5sum. Você vai querer: { tee < "$file" >(sha256sum >&3) | md5sum; } 3>&1. Consulte unix.stackexchange.com/q/153896/22565 para obter o problema inverso.
Stéphane Chazelas
6

É uma pena que o utilitário openssl não aceite vários comandos digest; Eu acho que executar o mesmo comando em vários arquivos é um padrão de uso mais comum. FWIW, a versão do utilitário openssl no meu sistema (Mepis 11) possui apenas comandos para sha e sha1, não nenhuma das outras variantes do sha. Mas eu tenho um programa chamado sha256sum, assim como md5sum.

Aqui está um simples programa Python, dual_hash.py, que faz o que você deseja. Um tamanho de bloco de 64k parece ser ideal para minha máquina (Intel Pentium 4 2.00GHz com 2G de RAM), YMMV. Para arquivos pequenos, sua velocidade é aproximadamente a mesma que executar md5sum e sha256sum em sucessão. Mas para arquivos maiores é significativamente mais rápido. Por exemplo, em um arquivo de 1967063040 bytes (uma imagem de disco de um cartão SD cheio de arquivos mp3), md5sum + sha256sum leva cerca de 1m44,9s, dual_hash.py leva 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))

Suponho que uma versão C / C ++ deste programa seria um pouco mais rápido, mas não muito, já que a maior parte do trabalho está sendo feito pelo módulo hashlib, que é escrito em C (ou C ++). E, como você observou acima, o gargalo para arquivos grandes é a velocidade de E / S.

PM 2Ring
fonte
Para um arquivo de 2.3G, esta versão possui uma velocidade comparável md5sume sha256sumcombinada (4.7s + 14.2s vs 18.7s para este script Python, arquivo em cache; 33.6s para execução a frio). 64KiB vs 1MiB não mudou a situação. Com o código comentado, 5,1s foram gastos em md5 (n = 3), 14,6s em sha1 (n = 3). Testado em um i5-460M com 8 GB de RAM. Eu acho que isso poderia ser melhorado usando mais threads.
Lekensteyn
C ou C ++ provavelmente não importará tanto tempo gasto em tempo de execução no módulo OpenSSL (usado pelo hashlib). Mais threads melhoram a velocidade, consulte este post sobre um script Python multiencadeado .
Lekensteyn
@PM 2Ring - Apenas uma nota. Após as instruções de impressão em sua função digest (), você precisa limpar pelo menos sha. Não sei dizer se você deve limpar o MD5 ou não. Eu usaria apenas "del sha". Caso contrário, todos os arquivos após o primeiro terão um hash incorreto. Para provar isso, faça um diretório tmp e copie um arquivo para ele. Agora faça 2 cópias desse arquivo e execute seu script. Você terá três hashes diferentes, o que não é o que você deseja. Edit: Eu pensei que a função estava lendo sobre um conjunto de arquivos, e não apenas lendo um único arquivo de cada vez ... Desconsidere esse uso. ;)
Terry Wendt
1
@TerryWendt Você me deixou preocupada por um segundo. :) Sim, digestsprocessa apenas um único arquivo em cada chamada. Portanto, mesmo se você o chamar em loop, criará novos contextos md5 e sha em cada chamada. FWIW, você pode desfrutar do meu hash SHA-256 recuperável .
usar o seguinte comando
5

Você sempre pode usar algo como o GNU paralelo :

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

Como alternativa, basta executar um dos dois em segundo plano:

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

Ou salve a saída em arquivos diferentes e execute vários trabalhos em segundo plano:

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

Que vai lançar o maior número md5sume sha256sumcasos que você tem arquivos e todos eles serão executados em paralelo, poupando sua produção para os nomes de arquivos correspondentes. Cuidado, porém, isso pode ficar pesado se você tiver muitos arquivos.

terdon
fonte
1
Veja o comentário para Marco, minha preocupação é que, embora o comando seja paralelo, o disco lento seja acessado duas vezes pelos mesmos dados.
Lekensteyn
Mas a existência do cache do disco não tornaria suas preocupações desnecessárias?
Twinkles
2
@ Twinkles Para citar Lekensteyn acima, "O problema dessa abordagem é que um comando pode ser mais rápido que o outro, resultando em um cache de disco que é esvaziado e reabastecido posteriormente com os mesmos dados".
Matt Nordhoff
2
@MattNordhoff Mais uma coisa que um planejador de E / S inteligente deve observar e otimizar. Alguém pode pensar: "Quão difícil pode ser para um agendador de E / S levar esse cenário em consideração?" Porém, com cenários diferentes o suficiente que um planejador de E / S deve levar em consideração, ele se torna repentinamente um problema difícil. Então, eu concordo que não se deve presumir que o cache cuidará do problema.
kasperd
1
Supondo que o IO seja significativamente mais lento do que qualquer uma das ferramentas envolvidas, as duas ferramentas devem ser reduzidas para a mesma velocidade por causa do IO. Portanto, se uma ferramenta conseguir obter alguns blocos de dados mais do que a outra, a outra ferramenta rapidamente alcançará os cálculos usando os dados no cache do disco. Essa teoria, eu adoraria ver alguns resultados experimentais provar isso ...
Liori
3

Por curiosidade se um script multi-threaded Python iria reduzir o tempo de execução, eu criei este digest.pyscript que usos threading.Thread, threading.Queuee hashlibpara calcular os hashes para vários arquivos.

A implementação Python multiencadeada é realmente um pouco mais rápida do que usar peecom coreutils. Java, por outro lado, é ... meh. Os resultados estão disponíveis nesta mensagem de confirmação :

Para comparação, para um arquivo de 2,3 GiB (min / avg / max / sd segundos para n = 10):

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

A saída de hash é compatível com a saída produzida por coreutils. Como o comprimento depende do algoritmo de hash, essa ferramenta não o imprime. Uso (para comparação, peetambém foi adicionado):

$ ./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
fonte
Eu sugeriria comparar pee "openssl sha256" "openssl md5" < file, mas, honestamente, eu apenas tentei, e não superou digest.py. Ele diminuiu a diferença, no entanto.
Matt Nordhoff
1

Jacksum é um utilitário gratuito e independente de plataforma para computação e verificação de somas de verificação, CRCs e hashes (resumos de mensagens), bem como registros de data e hora de arquivos. (extraído da página do manual jacksum )

Ele reconhece arquivos grandes, pode processar tamanhos de arquivos de até 8 Exabytes (= 8.000.000.000 Gigabytes), pressupondo o sistema operacional, respectivamente, o sistema de arquivos também reconhece arquivos grandes. (extraído de http://www.jonelo.de/java/jacksum/ )

Exemplo de uso:

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

Saída de amostra:

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

No ubuntu, execute o comando apt-get install jacksumpara obtê-lo.

Como alternativa, os códigos-fonte estão disponíveis em

pallxk
fonte
Embora isso produza as somas de verificação corretas, este programa Java calcula duas vezes mais lento que o coreutils. Veja esta mensagem de confirmação .
Lekensteyn