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()

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  -

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

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.


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.


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  -

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


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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.