Как получить сумму MD5 содержимого каталога в виде одной суммы?

Программа md5sum не предоставляет контрольные суммы для каталогов. Я хочу получить одну контрольную сумму MD5 для всего содержимого каталога, включая файлы в подкаталогах. То есть, одна комбинированная контрольная сумма была сделана из всех файлов. Есть ли способ сделать это?

140 голосов | спросил user17429 5 PMpThu, 05 Apr 2012 23:48:29 +040048Thursday 2012, 23:48:29

13 ответов


148

Правильный способ зависит от того, почему вы спрашиваете:

Вариант 1: Сравнить только данные

Если вам просто нужен хэш содержимого файла дерева, это сделает трюк:

$ find -s somedir -type f -exec md5sum {} \; | md5sum

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

К сожалению, find -s работает только с BSD find (1), используемым в Mac OS X, FreeBSD, NetBSD и OpenBSD. Чтобы получить что-то сопоставимое в системе с GNU или SUS find (1), вам нужно что-то более уродливое:

$ find somedir -type f -exec md5sum {} \; | sort -k 2 | md5sum

Мы заменили find -s на вызов sort. Бит -k 2 указывает, что он пропускает хэш MD5, поэтому он сортирует имена файлов, которые находятся в поле 2 по конец строки, с помощью sort расчёт.

В этой версии команды есть слабость, которая заключается в том, что она может смущаться, если у вас есть имена файлов с новыми символами в них, поскольку она будет выглядеть как несколько строк для вызова sort , Вариант find -s не имеет этой проблемы, поскольку обход дерева и сортировка происходят в одной программе, find.

В любом случае сортировка необходима, чтобы избежать ложных срабатываний. * ix файловые системы не поддерживают списки каталогов в стабильном, предсказуемом порядке; вы можете не понимать этого, используя ls и т. д., которые молча сортируют содержимое каталога для вас. find без -s или вызов sort будет распечатывать файлы в любом порядке, когда базовая файловая система возвращает их, что может привести к этой команде изменить значение хэша, когда все, что было изменено, - это порядок файлов в каталоге.

Вам может потребоваться изменить команды md5sum на md5 или другую хэш-функцию. Если вы выберете другую хеш-функцию и вам понадобится вторая форма команды для вашей системы, вам может потребоваться настроить команду sort, если ее выходная строка не имеет хеша, за которым следует имя файла, разделенное по пробелам. Например, вы не можете использовать старую программу Unix sum для этого, потому что ее вывод не включает имя файла.

Этот метод несколько неэффективен, вызывая md5sum N + 1 раз, где N - количество файлов в дереве, но это необходимая стоимость, чтобы избежать метаданных файлов хеширования и каталога.

Вариант 2. Сравнение данных и Метаданные

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

$ tar -cf - somedir | md5sum

Поскольку tar также видит права доступа к файлам, право собственности и т. д., это также обнаружит изменения в этих вещах, а не только изменения содержимого файла.

Этот метод значительно быстрее, так как он делает только один проход по дереву и запускает хеш-программу только один раз.

Как и в предыдущем методе find, tar будет обрабатывать имена файлов в том порядке, в котором их возвращает базовая файловая система. Вполне возможно, что в вашем приложении вы можете быть уверены, что это не произойдет. Я могу придумать, по крайней мере, три разных шаблона использования, где это может произойти. (Я не буду перечислять их, потому что мы попадаем в неопределенную область поведения. Каждая файловая система может быть различной здесь, даже с одной версии ОС до следующей.)

Если вы обнаружите, что получаете ложные срабатывания, я бы рекомендовал перейти к find | cpio в ответе Жиля.

ответил Warren Young 5 PMpThu, 05 Apr 2012 23:57:46 +040057Thursday 2012, 23:57:46
31

Контрольная сумма должна быть детерминированным и недвусмысленным представлением файлов в виде строки. Детерминистский означает, что если вы поместите одинаковые файлы в одно и то же место, вы получите тот же результат. Однозначный означает, что два разных набора файлов имеют разные представления.

Данные и метаданные

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

Решение заключается в сортировке имен файлов перед их архивированием. Если ваши имена файлов не содержат символы новой строки, вы можете запустить find | sort, чтобы перечислить их и добавить их в архив в этом порядке. Позаботьтесь о том, чтобы архиватор не переписывался в справочники. Вот примеры с POSIX pax, GNU tar и cpio:

 find | LC_ALL=C sort | pax -w -d | md5sum
find | LC_ALL=C sort | tar -cf - -T - --no-recursion | md5sum
find | LC_ALL=C sort | cpio -o | md5sum

Только имена и содержимое, низкотехнологичный способ

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

 { export LC_ALL=C;
  find -type f -exec wc -c {} \; | sort; echo;
  find -type f -exec md5sum {} + | sort; echo;
  find . -type d | sort; find . -type d | sort | md5sum;
} | md5sum

Мы включаем список каталогов в дополнение к списку контрольных сумм, так как в противном случае пустые каталоги будут невидимыми. Список файлов сортируется (в специфическом, воспроизводимом языке - благодаря Peter.O, чтобы напомнить мне об этом). echo разделяет две части (без этого вы можете создать несколько пустых каталогов, чье имя выглядит как вывод md5sum, который также может передаваться для обычных файлов). Мы также включаем список размеров файлов, чтобы избежать атак с расширением длины .

Кстати, MD5 устарел. Если это доступно, рассмотрите возможность использования SHA-2 или, по меньшей мере, SHA-1.

Имена и данные, поддерживающие символы новой строки в именах

Вот вариант кода выше, который опирается на инструменты GNU для разделения имен файлов на нулевые байты. Это позволяет именам файлов содержать новые строки. Утилиты дайджеста GNU цитируют специальные символы в их выводах, поэтому не будет неоднозначных строк новой строки.

 { export LC_ALL=C;
  du -0ab | sort -z; # file lengths, including directories (with length 0)
  echo | tr '\n' '\000'; # separator
  find -type f -exec sha256sum {} + | sort -z; # file hashes
  echo | tr '\n' '\000'; # separator
  echo "End of hashed data."; # End of input marker
} | sha256sum

Более надежный подход

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

 #! /usr/bin/env python
import hashlib, hmac, os, stat, sys
## Return the hash of the contents of the specified file, as a hex string
def file_hash(name):
    f = open(name)
    h = hashlib.sha256()
    while True:
        buf = f.read(16384)
        if len(buf) == 0: break
        h.update(buf)
    f.close()
    return h.hexdigest()
## Traverse the specified path and update the hash with a description of its
## name and contents
def traverse(h, path):
    rs = os.lstat(path)
    quoted_name = repr(path)
    if stat.S_ISDIR(rs.st_mode):
        h.update('dir ' + quoted_name + '\n')
        for entry in sorted(os.listdir(path)):
            traverse(h, os.path.join(path, entry))
    elif stat.S_ISREG(rs.st_mode):
        h.update('reg ' + quoted_name + ' ')
        h.update(str(rs.st_size) + ' ')
        h.update(file_hash(path) + '\n')
    else: pass # silently symlinks and other special files
h = hashlib.sha256()
for root in sys.argv[1:]: traverse(h, root)
h.update('end\n')
print h.hexdigest()
ответил Gilles 6 AMpFri, 06 Apr 2012 04:53:12 +040053Friday 2012, 04:53:12
11

Посмотрите md5deep . Некоторые из функций md5deep, которые могут вас заинтересовать:

  

Рекурсивная операция - md5deep способен рекурсивно проверять полное дерево каталогов. То есть, вычислите MD5 для каждого файла в каталоге и для каждого файла в каждом подкаталоге.

     

Режим сравнения - md5deep может принимать список известных хэшей и сравнивать их с набором входных файлов. Программа может отображать либо те входные файлы, которые соответствуют списку известных хэшей, либо те, которые не соответствуют.

     

...

ответил faultyserver 10 PMpTue, 10 Apr 2012 20:19:50 +040019Tuesday 2012, 20:19:50
7

Если ваша цель - найти различия между двумя каталогами, рассмотрите возможность использования diff.

Попробуйте следующее:

diff -qr dir1 dir2
ответил Deepak Mittal 6 AMpFri, 06 Apr 2012 09:24:01 +040024Friday 2012, 09:24:01
5

Вы можете хэшировать каждый файл рекурсивно, а затем хешировать полученный текст:

> md5deep -r -l . | sort | md5sum
d43417958e47758c6405b5098f151074 *-

md5deep требуется.

ответил Pavel Vlasov 14 PMpThu, 14 Apr 2016 16:34:09 +030034Thursday 2016, 16:34:09
3

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

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

Эта версия (ответ Уоррена Янга) помогла много, но моя версия md5sum выводит имя файла (по отношению к пути, из которого я запускал команду), и имена папок были разными, поэтому, несмотря на то, что контрольные суммы отдельных файлов совпадали, окончательная контрольная сумма не выполнялась.

Чтобы исправить это, в моем случае мне просто нужно было отключить имя файла из каждой строки вывода find (выберите только первое слово, разделенное пробелами, используя cut):

find -s somedir -type f -exec md5sum {} \; | cut -d" " -f1 | md5sum
ответил Nicole 11 Mayam13 2013, 04:34:04
3

Хорошая контрольная сумма дерева - это идентификатор дерева Git.

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

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

ответил eckes 11 AM00000050000003631 2013, 05:37:36
2

Я использую этот мой фрагмент для умеренных томов :

find . -xdev -type f -print0 | LC_COLLATE=C sort -z | xargs -0 cat | md5sum -

и этот для XXXL :

find . -xdev -type f -print0 | LC_COLLATE=C sort -z | xargs -0 tail -qc100 | md5sum -

ответил poige 10 PMpTue, 10 Apr 2012 21:26:52 +040026Tuesday 2012, 21:26:52
2

Решение :

$ pip install checksumdir
$ checksumdir -a md5 assets/js
981ac0bc890de594a9f2f40e00f13872
$ checksumdir -a sha1 assets/js
88cd20f115e31a1e1ae381f7291d0c8cd3b92fad

работает быстро и проще , затем bash-скриптинг.

см. документ: https://pypi.python.org/pypi/checksumdir/1.0 0,5

ответил DmitrySemenov 8 MaramTue, 08 Mar 2016 05:53:20 +03002016-03-08T05:53:20+03:0005 2016, 05:53:20
2

nix-hash из менеджера пакетов Nix

  

Команда nix-hash вычисляет криптографический хэш содержимого   каждого пути и печатает его на стандартном выходе. По умолчанию он вычисляет   хеш MD5, но доступны и другие алгоритмы хеширования. Хэш печатается в шестнадцатеричном формате.

     

Хэш вычисляется по сериализации каждого пути: дампа дерева файловой системы, внедренного в путь. Это позволяет каталогам   и символические ссылки для хэширования          а также обычные файлы. Дамп находится в формате NAR, создаваемом nix-store - dump. Таким образом, путь nix-hash дает тот же   криптографический хеш как nix-store          --dump путь | md5sum.

ответил Igor 27 J000000Wednesday16 2016, 19:48:33
1

Мне не нужны новые исполняемые файлы и неуклюжие решения, поэтому я беру его:

#!/bin/sh
# md5dir.sh by Camilo Martin, 2014-10-01.
# Give this a parameter and it will calculate an md5 of the directory's contents.
# It only takes into account file contents and paths relative to the directory's root.
# This means that two dirs with different names and locations can hash equally.

if [[ ! -d "$1" ]]; then
    echo "Usage: md5dir.sh <dir_name>"
    exit
fi

d="$(tr '\\' / <<< "$1" | tr -s / | sed 's-/$--')"
c=$((${#d} + 35))
find "$d" -type f -exec md5sum {} \; | cut -c 1-33,$c- | sort | md5sum | cut -c 1-32

Надеюсь, это поможет вам:)

ответил Camilo Martin 2 +04002014-10-02T06:13:26+04:00312014bEurope/MoscowThu, 02 Oct 2014 06:13:26 +0400 2014, 06:13:26
1

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

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

fingerprint analyze

Это приведет к генерации index.fingerprint в текущем каталоге, который включает контрольные суммы, имена файлов и размеры файлов. По умолчанию он использует как MD5, так и SHA1.256.

В будущем я надеюсь добавить поддержку Merkle Trees в Fingerprint, которая даст вам одну контрольную сумму верхнего уровня. Прямо сейчас вам нужно сохранить этот файл для проверки.

ответил ioquatix 7 J000000Thursday16 2016, 03:15:55
0

Прочный и чистый подход

  • Прежде всего, не загружает доступную память ! Хешируйте файл в кусках, а не кормите весь файл.
  • Различные подходы к различным потребностям /назначению (все ниже или выбрать, что когда-либо применяется):
    • Хешировать только имя записи всех записей в дереве каталогов
    • Хешируйте содержимое файла всех записей (оставляя метаподобный, номер inode, ctime, atime, mtime, размер и т. д., вы получаете идею)
    • Для символической ссылки ее содержимое является именем референта. Хеш или выберите пропустить
    • Следуйте или не следуйте (разрешенное имя) символическую ссылку во время хэширования содержимого записи
    • Если это каталог, его содержимое - это только записи в каталоге. Если рекурсивно проходить рекурсивно, они будут хешированы в конце концов, но должны ли хешировать имена записей этого уровня для маркировки этого каталога? Полезный в случаях использования, когда хэш требуется для быстрого определения изменения, без глубокого перемещения по содержимому. Примером может быть изменение имени файла, но остальное содержимое остается прежним, и все они довольно большие файлы.
    • Хорошо обрабатывайте большие файлы (опять же, помните о RAM)
    • Обрабатывать очень глубокие деревья каталогов (помните о дескрипторах открытых файлов)
    • Обрабатывать нестандартные имена файлов
    • Как работать с файлами, которые являются сокетами, каналами /FIFO, блочными устройствами, устройствами char? Должны также их хешировать?
    • Не обновляйте время доступа любой записи во время прохождения, потому что это будет побочным эффектом и контрпродуктивным (интуитивным?) для определенных случаев использования.

Это то, что у меня на голове, любой, кто провел некоторое время над этим, практически поймал бы другие ошибки и угловые случаи.

Вот инструмент (отказ от ответственности: я вношу в него вклад) dtreetrawl , очень свет в памяти, который обращается в большинстве случаев, может быть немного грубым по краям, но был весьма полезен.

Usage:
  dtreetrawl [OPTION...] "/trawl/me" [path2,...]

Help Options:
  -h, --help                Show help options

Application Options:
  -t, --terse               Produce a terse output; parsable.
  -d, --delim=:             Character or string delimiter/separator for terse output(default ':')
  -l, --max-level=N         Do not traverse tree beyond N level(s)
  --hash                    Hash the files to produce checksums(default is MD5).
  -c, --checksum=md5        Valid hashing algorithms: md5, sha1, sha256, sha512.
  -s, --hash-symlink        Include symbolic links' referent name while calculating the root checksum
  -R, --only-root-hash      Output only the root hash. Blank line if --hash is not set
  -N, --no-name-hash        Exclude path name while calculating the root checksum
  -F, --no-content-hash     Do not hash the contents of the file

Пример человеческого дружественного вывода:

...
... //clipped
...
/home/lab/linux-4.14-rc8/CREDITS
        Base name                    : CREDITS
        Level                        : 1
        Type                         : regular file
        Referent name                :
        File size                    : 98443 bytes
        I-node number                : 290850
        No. directory entries        : 0
        Permission (octal)           : 0644
        Link count                   : 1
        Ownership                    : UID=0, GID=0
        Preferred I/O block size     : 4096 bytes
        Blocks allocated             : 200
        Last status change           : Tue, 21 Nov 17 21:28:18 +0530
        Last file access             : Thu, 28 Dec 17 00:53:27 +0530
        Last file modification       : Tue, 21 Nov 17 21:28:18 +0530
        Hash                         : 9f0312d130016d103aa5fc9d16a2437e

Stats for /home/lab/linux-4.14-rc8:
        Elapsed time     : 1.305767 s
        Start time       : Sun, 07 Jan 18 03:42:39 +0530
        Root hash        : 434e93111ad6f9335bb4954bc8f4eca4
        Hash type        : md5
        Depth            : 8
        Total,
                size           : 66850916 bytes
                entries        : 12484
                directories    : 763
                regular files  : 11715
                symlinks       : 6
                block devices  : 0
                char devices   : 0
                sockets        : 0
                FIFOs/pipes    : 0
ответил six-k 7 Jpm1000000pmSun, 07 Jan 2018 14:27:07 +030018 2018, 14:27:07

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132