Come posso calcolare un checksum md5 di una directory?


133

Ho bisogno di calcolare un checksum di riepilogo md5 per tutti i file di un particolare tipo ( *.pyad esempio) collocati in una directory e in tutte le sottodirectory.

Qual è il modo migliore per farlo?

Modifica: le soluzioni proposte sono molto carine, ma non è esattamente quello di cui ho bisogno. Sto cercando una soluzione per ottenere un unico checksum di riepilogo che identificherà in modo univoco la directory nel suo insieme, incluso il contenuto di tutte le sue sottodirectory.


Dai un'occhiata a questo e questo per una spiegazione più dettagliata.
Luvieere,

3
Mi sembra una domanda da superutente.
Noldorin,

8
Nota che i checksum non identificano in modo univoco nulla.
Hosam Aly,

1
Perché dovresti avere due alberi di directory che possono essere o meno "uguali" che vuoi identificare in modo univoco? Il tempo di creazione / modifica / accesso dei file è importante? Il controllo versione è quello di cui hai veramente bisogno?
jmucchiello,

Ciò che conta davvero nel mio caso è la somiglianza dell'intero contenuto dell'albero delle directory, il che significa che AFAIK è il seguente: 1) il contenuto di qualsiasi file sotto l'albero delle directory non è stato modificato 2) nessun nuovo file è stato aggiunto all'albero delle directory 3) nessun file è stato cancellato
victorz il

Risposte:


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

Il comando find elenca tutti i file che terminano in .py. Il md5sum viene calcolato per ogni file .py. awk viene utilizzato per rimuovere md5sums (ignorando i nomi dei file, che potrebbero non essere univoci). Gli md5sums sono ordinati. Viene quindi restituito il md5sum di questo elenco ordinato.

Ho provato questo copiando una directory di test:

rsync -a ~/pybin/ ~/pybin2/

Ho rinominato alcuni dei file in ~ / pybin2.

Il find...md5sumcomando restituisce lo stesso output per entrambe le directory.

2bcf49a4d19ef9abd284311108d626f1  -

24
Si noti che lo stesso checksum verrà generato se un file viene rinominato. Quindi questo non corrisponde davvero a un "checksum che identificherà in modo univoco la directory nel suo insieme" se si considera il layout del file come parte della firma.
Valentin Milea,

1
è possibile modificare leggermente la riga di comando per aggiungere un prefisso a ciascun file con il nome del file (o ancora meglio, il percorso relativo del file da / path / a / dir /) in modo che venga preso in considerazione nel checksum finale.
Michael Zilbermann,

4
@ zim2001: Sì, potrebbe essere modificato, ma quando ho capito il problema (soprattutto a causa del commento dell'OP sotto la domanda), l'OP voleva che due directory fossero considerate uguali se il contenuto dei file fosse identico indipendentemente dal nome del file o anche percorso relativo.
unutbu,

@unutbu: lo so; Stavo reagendo alla nota precedente, di Valentin Milea.
Michael Zilbermann,

@ValentinMilea è sufficiente rimuovere la awk ...parte se si considera il layout come parte della firma.
segfault

166

Crea un file di archivio tar al volo e esegui il pipe per md5sum:

tar c dir | md5sum

Questo produce un unico md5sum che dovrebbe essere unico per la configurazione del tuo file e della tua sottodirectory. Nessun file viene creato sul disco.


25
@CharlesB con un unico check-sum non sai mai quale file è diverso. La domanda riguardava un unico check-sum per una directory.
Hawken,

17
ls -alR dir | md5sum. Questo è ancora meglio nessuna compressione solo una lettura. È unico perché il contenuto contiene il tempo mod e la dimensione del file;)
Sid

14
@ Daps0l - non c'è compressione nel mio comando. Devi aggiungere zper gzip o jper bzip2. Non ho fatto nessuno dei due.
ire_and_curses

7
Fai in modo che ciò comporti l'integrazione del timestamp dei file e di altre cose nel calcolo del checksum, non solo del contenuto dei file
Michael Zilbermann,

10
È carino, ma non funziona davvero. Non è possibile garantire che tarlo stesso set di file due volte, o su due computer diversi, produrrà lo stesso esatto risultato.
fletom,

46

Il suggerimento di ire_and_curses di utilizzare tar c <dir>ha alcuni problemi:

  • tar elabora le voci della directory nell'ordine in cui sono archiviate nel filesystem e non c'è modo di cambiare questo ordine. Questo può effettivamente produrre risultati completamente diversi se si dispone della "stessa" directory in luoghi diversi e non conosco alcun modo per risolvere questo problema (tar non può "ordinare" i suoi file di input in un ordine particolare).
  • Di solito mi importa se i numeri di groupid e ownerid sono uguali, non necessariamente se la rappresentazione di stringhe di gruppo / proprietario è la stessa. Ciò è in linea con ciò che ad esempio rsync -a --deletefa: sincronizza praticamente tutto (meno xattrs e acls), ma sincronizzerà il proprietario e il gruppo in base al loro ID, non alla rappresentazione della stringa. Quindi, se ti sei sincronizzato con un sistema diverso che non ha necessariamente gli stessi utenti / gruppi, dovresti aggiungere il --numeric-ownerflag a tar
  • tar includerà il nome file della directory che stai controllando, solo qualcosa di cui devi essere consapevole.

Fintanto che non esiste una soluzione per il primo problema (o a meno che tu non sia sicuro che non ti influenzi), non utilizzerei questo approccio.

Le findsoluzioni di base proposte sopra non sono utili perché includono solo file, non directory, che diventa un problema se il checksum deve tenere a mente le directory vuote.

Infine, la maggior parte delle soluzioni suggerite non ordina in modo coerente, perché le regole di confronto potrebbero essere diverse tra i sistemi.

Questa è la soluzione che mi è venuta in mente:

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

Note su questa soluzione:

  • L' LC_ALL=Cobiettivo è garantire un ordinamento affidabile tra i sistemi
  • Ciò non distingue tra una directory "nominata \ nwithanewline" e due directory "nominate" e "withanewline", ma la probabilità che ciò si verifichi sembra molto improbabile. Uno di solito risolve questo problema con una -print0bandiera, findma poiché ci sono altre cose in corso qui, posso solo vedere soluzioni che renderebbero il comando più complicato di quanto valga la pena.

PS: uno dei miei sistemi usa una scatola occupata limitata findche non supporta -exec-print0flag, e inoltre aggiunge '/' per indicare le directory, mentre findutils find non sembra, quindi per questa macchina devo eseguire:

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

Fortunatamente, non ho file / directory con le nuove righe nei loro nomi, quindi questo non è un problema su quel sistema.


1
+1: molto interessante! Stai dicendo che l'ordine potrebbe differire tra diversi tipi di filesystem o all'interno dello stesso filesystem?
ire_and_curses

2
tutti e due. dipende solo dall'ordine delle voci della directory all'interno di ciascuna directory. Le voci della directory AFAIK (nel filesystem) sono appena create nell'ordine in cui "crei i file nella directory". Un semplice esempio: $ mkdir a; toccare a / file-1; toccare a / file-2 $ mkdir b; toccare b / file-2; touch b / file-1 $ (cd a; tar -c. | md5sum) fb29e7af140aeea5a2647974f7cdec77 - $ (cd b; tar -c. | md5sum) a3a39358158a87059b9f111ccffa1023 -
Dieter_be

15

Se ti interessano solo i file e non le directory vuote, questo funziona bene:

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

10

Per completezza, c'è md5deep (1) ; non è direttamente applicabile a causa del requisito del filtro * .py ma dovrebbe andare bene insieme a find (1).


Quali parametri dovrei usare se volessi solo calcolare il checksum md5 di una directory?
Gabriel Fair,

9

Una soluzione che ha funzionato meglio per me:

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

Motivo per cui ha funzionato meglio per me:

  1. gestisce i nomi dei file contenenti spazi
  2. Ignora i metadati del filesystem
  3. Rileva se il file è stato rinominato

Problemi con altre risposte:

I metadati del filesystem non vengono ignorati per:

tar c - "$path" | md5sum

Non gestisce i nomi di file contenenti spazi né rileva se il file è stato rinominato:

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

4

Se vuoi un md5sum che copra l'intera directory, farei qualcosa del genere

cat *.py | md5sum 

1
Per i subdir usa qualcosa come cat **.py| md5sum
Ramon,

3

Controlla tutti i file, inclusi sia il contenuto che i nomi dei file

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

Come sopra, ma includendo solo i file * .py

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

Puoi anche seguire i link simbolici se vuoi

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

Altre opzioni che potresti considerare di utilizzare con 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 devi solo correre ls -lR *.py | md5sum. A meno che non ti preoccupi che qualcuno modifichi i file e li tocchi alle loro date originali e non cambi mai le dimensioni dei file, l'output da lsdovrebbe dirti se il file è cambiato. Il mio unix-foo è debole, quindi potresti aver bisogno di altri parametri della riga di comando per ottenere il tempo di creazione e il tempo di modifica per la stampa. lsti dirà anche se le autorizzazioni sui file sono cambiate (e sono sicuro che ci sono opzioni per disattivarlo se non ti interessa).


3
Questo può adattarsi ad alcuni casi d'uso, ma in genere si desidera che il checksum rifletta solo il contenuto e non le date. Ad esempio, se touchun file modifica la sua data (ma non il suo contenuto), mi aspetto che il checksum rimanga invariato.
Todd Owen,


1

Ho avuto lo stesso problema, quindi mi è venuto in mente questo script che elenca solo i md5sums dei file nella directory e se trova una sottodirectory viene eseguito di nuovo da lì, affinché ciò accada lo script deve essere in grado di scorrere la corrente directory o da una sottodirectory se detto argomento viene passato in $ 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

Sono abbastanza sicuro che questo script fallirà se i nomi dei file contengono spazi o virgolette. Lo trovo fastidioso con gli script bash, ma quello che faccio è cambiare l'IFS.
localhost

1

Se vuoi veramente indipendenza dagli attributi del filesystem e dalle differenze a livello di bit di alcune versioni tar, puoi usare cpio:

cpio -i -e theDirname | md5sum

0

Esistono altre due soluzioni:

Creare:

du -csxb /path | md5sum > file

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

Dai un'occhiata:

du -csxb /path | md5sum -c file

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

0

md5sumha funzionato bene per me, ma ho avuto problemi con sorte l'ordinamento dei nomi dei file. Quindi invece ho ordinato per md5sumrisultato. Avevo anche bisogno di escludere alcuni file per creare risultati comparabili.

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.