Как я могу рассчитать контрольную сумму md5 каталога?


133

Мне нужно рассчитать итоговую контрольную сумму md5 для всех файлов определенного типа ( *.pyнапример), размещенных в каталоге и во всех подкаталогах.

Каков наилучший способ сделать это?

Изменить: предлагаемые решения очень хороши, но это не совсем то, что мне нужно. Я ищу решение для получения единой сводной контрольной суммы, которая будет однозначно идентифицировать каталог в целом - включая содержимое всех его подкаталогов.


Взгляните на это и это для более подробного объяснения.
luvieere 01

3
Мне кажется, это вопрос суперпользователя.
Noldorin

8
Обратите внимание, что контрольные суммы ничего однозначно не идентифицируют.
Хосам Али,

1
Зачем вам два дерева каталогов, которые могут быть или не быть «одинаковыми», которые вы хотите однозначно идентифицировать? Имеет ли значение время создания / изменения / доступа к файлу? Вам действительно нужен контроль версий?
jmucchiello 01

Что действительно важно в моем случае, так это схожесть всего содержимого дерева каталогов, что означает AFAIK следующее: 1) содержимое любого файла в дереве каталогов не было изменено 2) в дерево каталогов не был добавлен новый файл 3) нет файла был удален
victorz 03

Ответы:


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

Команда find выводит список всех файлов с расширением .py. Md5sum вычисляется для каждого файла .py. awk используется для получения сумм md5 (игнорируя имена файлов, которые могут быть не уникальными). Суммы md5 отсортированы. Затем возвращается md5sum этого отсортированного списка.

Я проверил это, скопировав тестовый каталог:

rsync -a ~/pybin/ ~/pybin2/

Я переименовал некоторые файлы в ~ / pybin2.

Команда find...md5sumвозвращает одинаковые выходные данные для обоих каталогов.

2bcf49a4d19ef9abd284311108d626f1  -

24
Обратите внимание, что такая же контрольная сумма будет сгенерирована, если файл будет переименован. Таким образом, это не совсем подходит для «контрольной суммы, которая однозначно идентифицирует каталог в целом», если рассматривать макет файла как часть подписи.
Валентин Милеа

1
вы можете немного изменить командную строку, чтобы префикс каждой контрольной суммы файла с именем файла (или, что еще лучше, относительным путем к файлу от / path / to / dir /), чтобы он учитывался в окончательной контрольной сумме.
Михаэль Зильберманн

4
@ zim2001: Да, это можно изменить, но, как я понял проблему (особенно из-за комментария ОП под вопросом), ОП хотел, чтобы любые две директории считались равными, если содержимое файлов было одинаковым независимо от имени файла или даже относительный путь.
unutbu

@unutbu: я знаю; Я реагировал на предыдущую записку от Валентина Милеа.
Майкл Зильберманн

@ValentinMilea просто удалите awk ...часть, если вы считаете макет частью подписи.
segfault

166

Создайте файл архива tar на лету и направьте его md5sum:

tar c dir | md5sum

Это создает единственную md5sum, которая должна быть уникальной для вашего файла и настройки подкаталога. На диске не создаются файлы.


25
@CharlesB с единственной контрольной суммой, вы никогда не знаете, какой файл отличается. Вопрос касался единой контрольной суммы для справочника.
Hawken

17
ls -alR dir | md5sum, Это даже лучше без сжатия, а просто чтение. Это уникально, потому что контент содержит мод и размер файла;)
Сид

14
@ Daps0l - в моей команде нет сжатия. Вам нужно добавить zдля gzip или jдля bzip2. Я ни того, ни другого.
ire_and_curses

7
Позаботьтесь о том, чтобы при этом во время вычисления контрольной суммы учитывались временные метки файлов и другие элементы, а не только содержимое файлов
Майкл Зильберманн,

10
Это мило, но на самом деле не работает. Нет гарантии, что обработка tarодного и того же набора файлов дважды или на двух разных компьютерах приведет к одинаковому точному результату.
fletom 08

46

Предложение ire_and_curses об использовании tar c <dir>имеет некоторые проблемы:

  • tar обрабатывает записи каталога в том порядке, в котором они хранятся в файловой системе, и изменить этот порядок невозможно. Это эффективно может дать совершенно разные результаты, если у вас есть «один и тот же» каталог в разных местах, и я не знаю способа исправить это (tar не может «сортировать» свои входные файлы в определенном порядке).
  • Обычно меня волнует, совпадают ли номера groupid и ownerid, не обязательно, совпадают ли строковые представления группы / владельца. Это соответствует тому, что rsync -a --deleteделает, например : он синхронизирует практически все (за исключением xattrs и acls), но он будет синхронизировать владельца и группу на основе их идентификаторов, а не строкового представления. Поэтому, если вы синхронизировались с другой системой, которая не обязательно имеет одинаковых пользователей / группы, вы должны добавить --numeric-ownerфлаг в tar
  • tar будет включать имя файла каталога, который вы проверяете, просто то, о чем нужно знать.

Пока нет решения для первой проблемы (или если вы не уверены, что это не влияет на вас), я бы не стал использовать этот подход.

На findоснове решения , предложенные выше , не являются также не хорошо , потому что они включают в себя только файлы, не каталоги, что становится проблемой , если вас контрольной суммы следует иметь в виду пустые каталоги.

Наконец, большинство предлагаемых решений не сортируются последовательно, потому что параметры сортировки могут быть разными в разных системах.

Это решение, которое я придумал:

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

Примечания об этом решении:

  • LC_ALL=C для обеспечения надежного порядка сортировки в системах.
  • Это не делает различий между каталогом «named \ nwithanewline» и двумя каталогами «named» и «withanewline», но вероятность этого кажется очень маловероятной. Обычно это исправляют с помощью -print0флага для, findно, поскольку здесь происходят другие вещи, я могу видеть только решения, которые сделают команду более сложной, чем она того стоит.

PS: одна из моих систем использует ограниченный busybox, findкоторый не поддерживает -execни -print0флаги, а также добавляет '/' для обозначения каталогов, в то время как findutils find, похоже, не работает, поэтому для этой машины мне нужно запустить:

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

К счастью, у меня нет файлов / каталогов с символами новой строки в их именах, так что это не проблема для этой системы.


1
+1: очень интересно! Вы хотите сказать, что порядок может отличаться между разными типами файловых систем или внутри одной файловой системы?
ire_and_curses

2
обе. это зависит только от порядка записей в каталоге в каждом каталоге. Записи каталога AFAIK (в файловой системе) просто создаются в том порядке, в котором вы «создаете файлы в каталоге». Простой пример: $ mkdir a; коснитесь / файл-1; нажмите a / file-2 $ mkdir b; прикоснитесь к b / file-2; touch b / file-1 $ (cd a; tar -c. | md5sum) fb29e7af140aeea5a2647974f7cdec77 - $ (cd b; tar -c. | md5sum) a3a39358158a87059b9f111ccffa1023 -
Dieter_be,

15

Если вы заботитесь только о файлах, а не о пустых каталогах, это прекрасно работает:

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

10

Для полноты картины есть md5deep (1) ; он не применим напрямую из-за требований к фильтру * .py, но должен работать вместе с find (1).


Какие параметры я бы использовал, если бы хотел только вычислить контрольную сумму md5 каталога?
Габриэль Фэйр

9

Решение, которое работало лучше всего для меня:

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

Причина, почему это работает лучше всего для меня:

  1. обрабатывает имена файлов, содержащие пробелы
  2. Игнорирует метаданные файловой системы
  3. Определяет, был ли файл переименован

Вопросы с другими ответами:

Метаданные файловой системы не игнорируются для:

tar c - "$path" | md5sum

Не обрабатывает имена файлов, содержащие пробелы, и не определяет, был ли файл переименован:

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

4

Если вы хотите, чтобы одна md5sum охватывала весь каталог, я бы сделал что-то вроде

cat *.py | md5sum 

1
Для subdirs используйте что-то вроде cat **.py| md5sum
Ramon

3

Контрольная сумма всех файлов, включая содержимое и их имена

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

То же, что и выше, но только с файлами * .py

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

Вы также можете следовать символическим ссылкам, если хотите

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

Другие варианты, которые вы могли бы рассмотреть с помощью 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

Технически вам нужно только бежать ls -lR *.py | md5sum. Если вы не беспокоитесь о том, что кто-то изменил файлы и вернул их к исходным датам и никогда не изменил размеры файлов, вывод команды lsдолжен сообщить вам, изменился ли файл. Мой unix-foo слабый, поэтому вам могут потребоваться дополнительные параметры командной строки, чтобы получить время создания и время модификации для печати. lsтакже сообщит вам, изменились ли разрешения для файлов (и я уверен, что есть переключатели, чтобы отключить это, если вам это не интересно).


3
Это может соответствовать некоторым вариантам использования, но обычно вы хотите, чтобы контрольная сумма отражала только содержимое, а не даты вообще. Например, если я touchизменяю дату (но не содержимое) файла, то я ожидаю, что контрольная сумма не изменится .
Тодд Оуэн


1

У меня была та же проблема, поэтому я придумал этот скрипт, который просто перечисляет md5-суммы файлов в каталоге, и если он находит подкаталог, он запускается снова оттуда, чтобы это произошло, скрипт должен иметь возможность запускать текущий каталог или из подкаталога, если указанный аргумент передается в $ 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

Я почти уверен, что этот сценарий не сработает, если имена файлов содержат пробелы или кавычки. Меня это раздражает в сценариях bash, но я просто меняю IFS.
localhost

1

Если вам нужна действительно независимость от атрибутов файловой системы и от различий на уровне битов некоторых версий tar, вы можете использовать cpio:

cpio -i -e theDirname | md5sum

0

Есть еще два решения:

Создайте:

du -csxb /path | md5sum > file

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

Проверьте:

du -csxb /path | md5sum -c file

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

0

md5sumу меня работал нормально, но у меня были проблемы с sortсортировкой имен файлов. Поэтому вместо этого я отсортировал по md5sumрезультату. Мне также нужно было исключить некоторые файлы, чтобы получить сопоставимые результаты.

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.