Como posso calcular uma soma de verificação md5 de um diretório?


133

Preciso calcular uma soma de verificação md5 resumida para todos os arquivos de um tipo específico ( *.pypor exemplo) colocados em um diretório e em todos os subdiretórios.

Qual o melhor jeito pra fazer isso?

Edit: As soluções propostas são muito boas, mas isso não é exatamente o que eu preciso. Estou procurando uma solução para obter uma única soma de verificação de resumo que identifique exclusivamente o diretório como um todo - incluindo o conteúdo de todos os seus subdiretórios.


Veja isto e isto para obter uma explicação mais detalhada.
Luvieere

3
Parece uma pergunta de superusuário para mim.
Noldorin

8
Observe que as somas de verificação não identificam nada exclusivamente .
276 Hosam Aly

1
Por que você teria duas árvores de diretório que podem ou não ser "iguais" que você deseja identificar exclusivamente? O arquivo cria / modifica / tempo de acesso importa? O controle de versão é o que você realmente precisa?
jmucchiello

O que realmente importa no meu caso é a semelhança de todo o conteúdo da árvore de diretórios, o que significa AFAIK o seguinte: 1) o conteúdo de qualquer arquivo na árvore de diretórios não foi alterado 2) nenhum novo arquivo foi adicionado à árvore de diretórios 3) nenhum arquivo foi excluído
victorz 3/11/2009

Respostas:


152
find /path/to/dir/ -type f -name "*.py" -exec md5sum {} + | awk '{print $1}' | sort | md5sum

O comando find lista todos os arquivos que terminam em .py. O md5sum é calculado para cada arquivo .py. O awk é usado para selecionar os md5sums (ignorando os nomes dos arquivos, que podem não ser únicos). Os md5sums são classificados. O md5sum dessa lista classificada é retornado.

Eu testei isso copiando um diretório de teste:

rsync -a ~/pybin/ ~/pybin2/

Renomeei alguns dos arquivos em ~ / pybin2.

O find...md5sumcomando retorna a mesma saída para os dois diretórios.

2bcf49a4d19ef9abd284311108d626f1  -

24
Observe que a mesma soma de verificação será gerada se um arquivo for renomeado. Portanto, isso realmente não se encaixa em uma "soma de verificação que identificará exclusivamente o diretório como um todo" se você considerar o layout do arquivo como parte da assinatura.
Valentin Milea

1
você pode alterar levemente a linha de comando para prefixar cada soma de verificação de arquivo com o nome do arquivo (ou, melhor ainda, o caminho relativo do arquivo de / path / para / dir /) para que isso seja levado em consideração na soma de verificação final.
Michael Zilbermann

4
@ zim2001: Sim, poderia ser alterado, mas como eu entendi o problema (especialmente devido ao comentário do OP na pergunta), o OP queria que dois diretórios fossem considerados iguais se o conteúdo dos arquivos fosse idêntico, independentemente do nome do arquivo ou caminho relativo.
Unutbu

@unutbu: eu sei; eu estava reagindo à nota anterior, de Valentin Milea.
Michael Zilbermann

O @ValentinMilea apenas remove a awk ...peça se considerar a parte de layout da assinatura.
Segfault 01/07/2013

166

Crie um arquivo tar em tempo real e faça o pipe para md5sum:

tar c dir | md5sum

Isso produz um único md5sum que deve ser exclusivo para a sua configuração de arquivo e subdiretório. Nenhum arquivo é criado no disco.


25
@CharlesB com uma única soma de verificação, você nunca sabe qual arquivo é diferente. A questão era sobre uma soma de verificação única para um diretório.
Hawken

17
ls -alR dir | md5sum. Isso é ainda melhor sem compactação apenas uma leitura. Ele é único porque o conteúdo contém o tempo eo tamanho do arquivo mod;)
Sid

14
@ Daps0l - não há compressão no meu comando. Você precisa adicionar zpara gzip ou jbzip2. Eu também não fiz.
ire_and_curses

7
Tome cuidado para que fazer isso seria integrar o timestamp dos arquivos e outras coisas na computação checksum, não só o conteúdo dos arquivos
Michael Zilbermann

10
Isso é fofo, mas realmente não funciona. Não há garantia de que taro mesmo conjunto de arquivos duas vezes ou em dois computadores diferentes produza o mesmo resultado exato.
fletom

46

A sugestão de ire_and_curses de usar tar c <dir>tem alguns problemas:

  • O tar processa as entradas do diretório na ordem em que são armazenadas no sistema de arquivos e não há como alterar essa ordem. Isso efetivamente pode gerar resultados completamente diferentes se você tiver o diretório "mesmo" em locais diferentes, e não sei como corrigir isso (o tar não pode "classificar" seus arquivos de entrada em uma ordem específica).
  • Eu geralmente me importo se os números groupid e ownerid são os mesmos, não necessariamente se a representação em string do grupo / owner é a mesma. Isso está alinhado com o que, por exemplo, rsync -a --deletefaz: sincroniza praticamente tudo (menos xattrs e acls), mas sincroniza o proprietário e o grupo com base em seu ID, não na representação de cadeias. Portanto, se você sincronizou com um sistema diferente que não necessariamente tem os mesmos usuários / grupos, adicione o --numeric-ownersinalizador ao tar
  • O tar incluirá o nome do arquivo do diretório que você está verificando, apenas algo para estar ciente.

Enquanto não houver solução para o primeiro problema (ou a menos que você tenha certeza de que isso não o afeta), eu não usaria essa abordagem.

As findsoluções baseadas propostas acima também não são boas, pois incluem apenas arquivos, não diretórios, o que se torna um problema se você usar a soma de verificação em diretórios vazios.

Por fim, a maioria das soluções sugeridas não é classificada de maneira consistente, pois o agrupamento pode ser diferente entre os sistemas.

Esta é a solução que eu vim com:

dir=<mydir>; (find "$dir" -type f -exec md5sum {} +; find "$dir" -type d) | LC_ALL=C sort | md5sum

Notas sobre esta solução:

  • O LC_ALL=Cobjetivo é garantir uma ordem de classificação confiável entre os sistemas
  • Isso não diferencia entre um diretório "nomeado \ nwithanewline" e dois diretórios "nomeado" e "withanewline", mas a chance de isso ocorrer parece muito improvável. Geralmente, isso é corrigido com um -print0sinalizador, findmas, como há outras coisas acontecendo aqui, só consigo ver soluções que tornariam o comando mais complicado do que vale a pena.

PS: um dos meus sistemas usa um busybox limitado findque não suporta -execnem -print0sinalizadores, e também adiciona '/' para indicar diretórios, enquanto o findutils find não parece, então, para esta máquina, preciso executar:

dir=<mydir>; (find "$dir" -type f | while read f; do md5sum "$f"; done; find "$dir" -type d | sed 's#/$##') | LC_ALL=C sort | md5sum

Felizmente, não tenho arquivos / diretórios com novas linhas em seus nomes, portanto isso não é um problema nesse sistema.


1
+1: Muito interessante! Você está dizendo que a ordem pode diferir entre diferentes tipos de sistema de arquivos ou dentro do mesmo sistema de arquivos?
ire_and_curses

2
ambos. depende apenas da ordem das entradas do diretório em cada diretório. As entradas do diretório AFAIK (no sistema de arquivos) são criadas apenas na ordem em que você "cria arquivos no diretório". Um exemplo simples: $ mkdir a; toque em / arquivo-1; toque em / file-2 $ mkdir b; toque em b / arquivo-2; touch b / file-1 $ (cd a; tar -c. | md5sum) fb29e7af140aeea5a2647974f7cdec77 - $ (cd b; tar -c. | md5sum) a3a39358158a87059b9f111ccffa1023 -
Dieter_be

15

Se você se importa apenas com arquivos e não com diretórios vazios, isso funciona muito bem:

find /path -type f | sort -u | xargs cat | md5sum

10

Por uma questão de completude, há md5deep (1) ; não é diretamente aplicável devido ao requisito de filtro * .py, mas deve funcionar bem com find (1).


Quais parâmetros eu usaria se quisesse apenas calcular a soma de verificação md5 de um diretório?
Gabriel Fair

9

Uma solução que funcionou melhor para mim:

find "$path" -type f -print0 | sort -z | xargs -r0 md5sum | md5sum

Razão pela qual funcionou melhor para mim:

  1. lida com nomes de arquivos contendo espaços
  2. Ignora os metadados do sistema de arquivos
  3. Detecta se o arquivo foi renomeado

Problemas com outras respostas:

Os metadados do sistema de arquivos não são ignorados para:

tar c - "$path" | md5sum

Não manipula nomes de arquivos que contenham espaços nem detecta se o arquivo foi renomeado:

find /path -type f | sort -u | xargs cat | md5sum

4

Se você quiser um md5sum abrangendo todo o diretório, eu faria algo como

cat *.py | md5sum 

1
Para subdiretórios, use algo como cat **.py| md5sum
Ramon '

3

Soma de verificação todos os arquivos, incluindo o conteúdo e seus nomes de arquivo

grep -ar -e . /your/dir | md5sum | cut -c-32

O mesmo que acima, mas incluindo apenas arquivos * .py

grep -ar -e . --include="*.py" /your/dir | md5sum | cut -c-32

Você também pode seguir links simbólicos, se quiser

grep -aR -e . /your/dir | md5sum | cut -c-32

Outras opções que você pode considerar usar com grep

-s, --no-messages         suppress error messages
-D, --devices=ACTION      how to handle devices, FIFOs and sockets;
-Z, --null                print 0 byte after FILE name
-U, --binary              do not strip CR characters at EOL (MSDOS/Windows)


2

Tecnicamente, você só precisa executar ls -lR *.py | md5sum. A menos que você esteja preocupado com alguém modificando os arquivos e retornando-os às datas originais e nunca alterando o tamanho dos arquivos, a saída de lsdeve informar se o arquivo foi alterado. Meu unix-foo é fraco, então você pode precisar de mais alguns parâmetros de linha de comando para obter o tempo de criação e o tempo de modificação para impressão. lstambém informará se as permissões dos arquivos foram alteradas (e tenho certeza de que existem opções para desativá-lo, se você não se importar com isso).


3
Isso pode se encaixar em alguns casos de uso, mas geralmente você deseja que a soma de verificação reflita apenas o conteúdo e não as datas. Por exemplo, se touchum arquivo alterar sua data (mas não seu conteúdo), esperaria que a soma de verificação permanecesse inalterada.
Todd Owen


1

Eu tive o mesmo problema, então criei este script que apenas lista os md5sums dos arquivos no diretório e, se encontrar um subdiretório, será executado novamente a partir daí, para que isso aconteça, o script deve ser capaz de executar os procedimentos atuais. diretório ou de um subdiretório se o argumento for passado em $ 1

#!/bin/bash

if [ -z "$1" ] ; then

# loop in current dir
ls | while read line; do
  ecriv=`pwd`"/"$line
if [ -f $ecriv ] ; then
    md5sum "$ecriv"
elif [ -d $ecriv ] ; then
    sh myScript "$line" # call this script again
fi

done


else # if a directory is specified in argument $1

ls "$1" | while read line; do
  ecriv=`pwd`"/$1/"$line

if [ -f $ecriv ] ; then
    md5sum "$ecriv"

elif [ -d $ecriv ] ; then
    sh myScript "$line"
fi

done


fi

Tenho certeza de que esse script falhará se os nomes de arquivos contiverem espaços ou aspas. Acho isso irritante com scripts bash, mas o que faço é alterar o IFS.
localhost

1

Se você quer realmente independência dos atributos do sistema de arquivos e das diferenças no nível de bit de algumas versões tar, você pode usar o cpio:

cpio -i -e theDirname | md5sum

0

Existem mais duas soluções:

Crio:

du -csxb /path | md5sum > file

ls -alR -I dev -I run -I sys -I tmp -I proc /path | md5sum > /tmp/file

Verifica:

du -csxb /path | md5sum -c file

ls -alR -I dev -I run -I sys -I tmp -I proc /path | md5sum -c /tmp/file

0

md5sumfuncionou bem para mim, mas tive problemas com a sortclassificação de nomes de arquivos. Então, em vez disso, classifiquei por md5sumresultado. Eu também precisava excluir alguns arquivos para criar resultados comparáveis.

find . -type f -print0 \ | xargs -r0 md5sum \ | grep -v ".env" \ | grep -v "vendor/autoload.php" \ | grep -v "vendor/composer/" \ | sort -d \ | md5sum

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.