Comment puis-je calculer une somme de contrôle md5 d'un répertoire?

133

J'ai besoin de calculer une somme de contrôle md5 récapitulative pour tous les fichiers d'un type particulier ( *.pypar exemple) placés sous un répertoire et tous les sous-répertoires.

Quelle est la meilleure façon de le faire?

Edit: Les solutions proposées sont très sympas, mais ce n'est pas exactement ce dont j'ai besoin. Je recherche une solution pour obtenir une somme de contrôle récapitulative unique qui identifiera de manière unique le répertoire dans son ensemble, y compris le contenu de tous ses sous-répertoires.

Victorz
la source
Jetez un œil à ceci et ceci pour une explication plus détaillée.
luvieere
3
Cela me semble être une question de superutilisateur.
Noldorin
8
Notez que les sommes de contrôle n'identifient rien de manière unique .
Hosam Aly
1
Pourquoi auriez-vous deux arborescences de répertoires qui peuvent ou non être «identiques» que vous souhaitez identifier de manière unique? Le temps de création / modification / accès du fichier est-il important? Le contrôle de version est-il ce dont vous avez vraiment besoin?
jmucchiello
Ce qui compte vraiment dans mon cas, c'est la similitude de tout le contenu de l'arborescence de répertoires, ce qui signifie AFAIK ce qui suit: 1) le contenu de tout fichier sous l'arborescence de répertoires n'a pas été modifié 2) aucun nouveau fichier n'a été ajouté à l'arborescence de répertoires 3) aucun fichier a été supprimé
victorz

Réponses:

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

La commande find répertorie tous les fichiers qui se terminent par .py. La somme md5 est calculée pour chaque fichier .py. awk est utilisé pour sélectionner les md5sums (en ignorant les noms de fichiers, qui peuvent ne pas être uniques). Les md5sums sont triés. La somme md5 de cette liste triée est ensuite renvoyée.

J'ai testé cela en copiant un répertoire de test:

rsync -a ~/pybin/ ~/pybin2/

J'ai renommé certains des fichiers dans ~ / pybin2.

La find...md5sumcommande renvoie la même sortie pour les deux répertoires.

2bcf49a4d19ef9abd284311108d626f1  -
unutbu
la source
24
Notez que la même somme de contrôle sera générée si un fichier est renommé. Donc, cela ne correspond pas vraiment à une "somme de contrôle qui identifiera de manière unique le répertoire dans son ensemble" si vous considérez la mise en page du fichier comme une partie de la signature.
Valentin Milea
1
vous pouvez légèrement modifier la ligne de commande pour préfixer chaque somme de contrôle de fichier avec le nom du fichier (ou encore mieux, le chemin relatif du fichier de / chemin / vers / dir /) afin qu'il soit pris en compte dans la somme de contrôle finale.
Michael Zilbermann
4
@ zim2001: Oui, cela pourrait être modifié, mais comme j'ai compris le problème (en particulier en raison du commentaire de l'OP sous la question), l'OP voulait que deux répertoires soient considérés comme égaux si le contenu des fichiers était identique quel que soit le nom de fichier ou même chemin relatif.
unutbu
@unutbu: Je sais; je réagissais à la note précédente, de Valentin Milea.
Michael Zilbermann
@ValentinMilea supprime simplement la awk ...partie si vous considérez la mise en page comme une partie de la signature.
segfault
166

Créez un fichier d'archive tar à la volée et dirigez-le vers md5sum:

tar c dir | md5sum

Cela produit une seule somme md5 qui doit être unique à votre configuration de fichier et de sous-répertoire. Aucun fichier n'est créé sur le disque.

ire_and_curses
la source
25
@CharlesB avec une seule somme de contrôle, vous ne savez jamais quel fichier est différent. La question portait sur une seule somme de contrôle pour un annuaire.
Hawken
17
ls -alR dir | md5sum. C'est encore mieux sans compression juste une lecture. Il est unique car le contenu contient l'heure de modification et la taille du fichier;)
Sid
14
@ Daps0l - il n'y a pas de compression dans ma commande. Vous devez ajouter zpour gzip ou jpour bzip2. Je n'ai fait ni l'un ni l'autre.
ire_and_curses
7
Veillez à ce que cela intègre l'horodatage des fichiers et d'autres éléments dans le calcul de la somme de contrôle, pas seulement le contenu des fichiers
Michael Zilbermann
10
C'est mignon, mais ça ne marche pas vraiment. Il n'y a aucune garantie que le fait d' taringérer le même ensemble de fichiers deux fois, ou sur deux ordinateurs différents, produira exactement le même résultat.
fletom
46

La suggestion d'utiliser ire_and_curses pose tar c <dir>quelques problèmes:

  • tar traite les entrées du répertoire dans l'ordre dans lequel elles sont stockées dans le système de fichiers, et il n'y a aucun moyen de changer cet ordre. Cela peut effectivement donner des résultats complètement différents si vous avez le "même" répertoire à différents endroits, et je ne connais aucun moyen de résoudre ce problème (tar ne peut pas "trier" ses fichiers d'entrée dans un ordre particulier).
  • Je me soucie généralement de savoir si les numéros groupid et ownerid sont identiques, pas nécessairement si la représentation sous forme de chaîne du groupe / propriétaire est la même. Ceci est conforme à ce que fait par exemple rsync -a --delete: il synchronise pratiquement tout (moins xattrs et acls), mais il synchronisera le propriétaire et le groupe en fonction de leur ID, pas sur la représentation sous forme de chaîne. Donc, si vous avez synchronisé avec un système différent qui n'a pas nécessairement les mêmes utilisateurs / groupes, vous devez ajouter l' --numeric-ownerindicateur à tar
  • tar inclura le nom de fichier du répertoire que vous vérifiez lui-même, juste quelque chose dont vous devez être conscient.

Tant qu'il n'y a pas de solution pour le premier problème (ou à moins que vous ne soyez sûr que cela ne vous affecte pas), je n'utiliserais pas cette approche.

Les findsolutions basées proposées ci-dessus ne sont pas non plus bonnes car elles n'incluent que des fichiers, pas des répertoires, ce qui devient un problème si la somme de contrôle doit garder à l'esprit les répertoires vides.

Enfin, la plupart des solutions suggérées ne trient pas de manière cohérente, car le classement peut être différent d'un système à l'autre.

Voici la solution que j'ai trouvée:

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

Remarques sur cette solution:

  • Le but LC_ALL=Cest d'assurer un ordre de tri fiable entre les systèmes
  • Cela ne fait pas la différence entre un répertoire «nommé \ nwithanewline» et deux répertoires «nommé» et «withanewline», mais la probabilité que cela se produise semble très improbable. On corrige généralement cela avec un -print0indicateur pour, findmais comme il se passe d'autres choses ici, je ne peux voir que des solutions qui rendraient la commande plus compliquée que ça en vaut la peine.

PS: l'un de mes systèmes utilise une busybox limitée findqui ne prend pas en charge -execni les -print0indicateurs, et ajoute également '/' pour désigner les répertoires, alors que findutils ne semble pas le faire, donc pour cette machine, je dois exécuter:

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

Heureusement, je n'ai pas de fichiers / répertoires avec des nouvelles lignes dans leurs noms, donc ce n'est pas un problème sur ce système.

Dieter_be
la source
1
+1: Très intéressant! Êtes-vous en train de dire que l'ordre peut différer entre différents types de systèmes de fichiers ou au sein du même système de fichiers?
ire_and_curses
2
tous les deux. cela dépend simplement de l'ordre des entrées du répertoire dans chaque répertoire. Les entrées du répertoire AFAIK (dans le système de fichiers) sont simplement créées dans l'ordre dans lequel vous "créez des fichiers dans le répertoire". Un exemple simple: $ mkdir a; touchez un / fichier-1; touchez a / file-2 $ mkdir b; touchez b / fichier-2; touch b / file-1 $ (cd a; tar -c. | md5sum) fb29e7af140aeea5a2647974f7cdec77 - $ (cd b; tar -c. | md5sum) a3a39358158a87059b9f111ccffa1023 -
Dieter_be
15

Si vous ne vous souciez que des fichiers et non des répertoires vides, cela fonctionne bien:

find /path -type f | sort -u | xargs cat | md5sum
Tesujimath
la source
10

Par souci d'exhaustivité, il y a md5deep (1) ; il n'est pas directement applicable en raison de l'exigence de filtre * .py mais devrait fonctionner avec find (1).

Michael Shigorin
la source
Quels paramètres utiliserais-je si je voulais uniquement calculer la somme de contrôle md5 d'un répertoire?
Gabriel Fair
9

Une solution qui a fonctionné le mieux pour moi:

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

Raison pour laquelle cela a fonctionné le mieux pour moi:

  1. gère les noms de fichiers contenant des espaces
  2. Ignore les métadonnées du système de fichiers
  3. Détecte si le fichier a été renommé

Problèmes avec d'autres réponses:

Les métadonnées du système de fichiers ne sont pas ignorées pour:

tar c - "$path" | md5sum

Ne gère pas les noms de fichiers contenant des espaces et ne détecte pas si le fichier a été renommé:

find /path -type f | sort -u | xargs cat | md5sum
Tiago Lopo
la source
4

Si vous voulez un md5sum couvrant tout le répertoire, je ferais quelque chose comme

cat *.py | md5sum 
Ramon
la source
1
Pour les sous-répertoires, utilisez quelque chose comme cat **.py| md5sum
Ramon
3

Somme de contrôle de tous les fichiers, y compris le contenu et leurs noms de fichiers

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

Identique à ci-dessus, mais n'incluant que les fichiers * .py

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

Vous pouvez également suivre des liens symboliques si vous le souhaitez

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

Autres options que vous pourriez envisager d'utiliser avec 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)
moander
la source
2

Trouver GNU

find /path -type f -name "*.py" -exec md5sum "{}" +;
ghostdog74
la source
Le dernier jeton devrait-il être \; ?
Dan Moulding
2

Techniquement, il vous suffit de courir ls -lR *.py | md5sum. À moins que vous ne craigniez que quelqu'un modifie les fichiers et les ramène à leurs dates d'origine et ne change jamais la taille des fichiers, la sortie de lsdevrait vous dire si le fichier a changé. Mon unix-foo est faible donc vous aurez peut-être besoin de plus de paramètres de ligne de commande pour obtenir l'heure de création et l'heure de modification à imprimer. lsvous dira également si les autorisations sur les fichiers ont changé (et je suis sûr qu'il existe des commutateurs pour désactiver cela si vous ne vous souciez pas de cela).

jmucchiello
la source
3
Cela peut convenir à certains cas d'utilisation, mais en général, vous voudrez que la somme de contrôle ne reflète que le contenu et non les dates du tout. Par exemple, si j'ai touchun fichier pour changer sa date (mais pas son contenu), alors je m'attendrais à ce que la somme de contrôle soit inchangée.
Todd Owen
2

Utilisation md5deep:

md5deep -r FOLDER | awk '{print $1}' | sort | md5sum

n'a pas vraiment d'importance
la source
1

J'ai eu le même problème alors j'ai créé ce script qui répertorie simplement les md5sums des fichiers dans le répertoire et s'il trouve un sous-répertoire, il s'exécute à nouveau à partir de là, pour que cela se produise, le script doit être capable de parcourir le courant répertoire ou d'un sous-répertoire si ledit argument est passé dans $ 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
Alan
la source
Je suis presque sûr que ce script échouera si les noms de fichiers contiennent des espaces ou des guillemets. Je trouve cela ennuyeux avec les scripts bash, mais ce que je fais, c'est changer l'IFS.
localhost
1

Si vous voulez vraiment être indépendant des attributs du système de fichiers et des différences au niveau du bit de certaines versions tar, vous pouvez utiliser cpio:

cpio -i -e theDirname | md5sum
peterh - Réintégrer Monica
la source
0

Il existe deux autres solutions:

Créer:

du -csxb /path | md5sum > file

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

Vérifier:

du -csxb /path | md5sum -c file

ls -alR -I dev -I run -I sys -I tmp -I proc /path | md5sum -c /tmp/file
pseudo
la source
0

md5suma bien fonctionné pour moi, mais j'ai eu des problèmes avec sortet de tri des noms de fichiers. Donc, à la place, j'ai trié par md5sumrésultat. J'avais également besoin d'exclure certains fichiers afin de créer des résultats comparables.

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

Singe
la source