Програма md5sum не забезпечує контрольні суми для каталогів. Я хочу отримати єдину контрольну суму MD5 на весь вміст каталогу, включаючи файли в підкаталогах. Тобто одна комбінована контрольна сума складається з усіх файлів. Чи є спосіб це зробити?
Програма md5sum не забезпечує контрольні суми для каталогів. Я хочу отримати єдину контрольну суму MD5 на весь вміст каталогу, включаючи файли в підкаталогах. Тобто одна комбінована контрольна сума складається з усіх файлів. Чи є спосіб це зробити?
Відповіді:
Правильний спосіб залежить від того, чому саме ви запитуєте:
Якщо вам просто потрібен хеш вмісту файлу дерева, це зробить фокус:
$ find -s somedir -type f -exec md5sum {} \; | md5sum
Це спочатку підсумовує весь вміст файлу окремо, у передбачуваному порядку, потім передає цей список назв файлів та хешів MD5, щоб бути хешованим, даючи єдине значення, яке змінюється лише при зміні вмісту одного з файлів у дереві.
На жаль, find -s
працює лише з BSD find (1), який використовується в macOS, 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
розрахунками «s.
У цій версії команди є слабкість, яка полягає в тому, що вона може заплутатися, якщо у вас є імена файлів з новими рядками в них, оскільки це буде схоже на кілька рядків для sort
виклику. find -s
Варіант не має цієї проблеми, так як обхід дерева і сортування відбувається в протягом однієї і тієї ж програми, find
.
У будь-якому випадку сортування необхідне, щоб уникнути помилкових позитивів: найпоширеніші файлові системи Unix / Linux не підтримують списки каталогів у стабільному передбачуваному порядку. Ви можете не усвідомити це з використанням ls
та таких, які мовчки сортують вміст каталогу для вас. find
без -s
або з sort
викликом буде друкувати файли в будь-якому порядку, повертаючи їх в основну файлову систему, що призведе до того, що ця команда надасть змінене хеш-значення, якщо порядок файлів, наданих їй як вхід, зміниться.
Можливо, вам знадобиться змінити md5sum
команди на md5
будь-яку іншу хеш-функцію. Якщо ви вибрали іншу хеш-функцію і вам потрібна друга форма команди для вашої системи, вам може знадобитися відповідно налаштувати sort
команду. Ще одна пастка полягає в тому, що деякі програми зведення даних взагалі не виписують ім’я файлу, головним прикладом є стара sum
програма Unix .
Цей метод дещо неефективний, викликаючи md5sum
N + 1 раз, де N - кількість файлів у дереві, але це необхідна вартість, щоб уникнути хешування метаданих файлів та каталогів.
Якщо вам потрібно мати можливість виявити, що щось на дереві змінилося, а не лише вміст файлу, попросіть tar
спакувати вміст каталогу для вас, а потім надішліть його md5sum
:
$ tar -cf - somedir | md5sum
Оскільки tar
також бачать дозволи файлів, право власності тощо, це також виявить зміни в цих речах, а не лише зміни вмісту файлу.
Цей метод значно швидший, оскільки він робить лише один прохід по дереву і запускає хеш-програму лише один раз.
Як і в find
основі вищевказаного методу, tar
збирається обробляти імена файлів у порядку, коли їх повертає файлова система. Цілком можливо, що у вашій заяві ви можете бути впевнені, що ви цього не зробите. Я можу придумати щонайменше три різних схеми використання, де це, мабуть, так і є. (Я не збираюсь їх перераховувати, тому що ми потрапляємо на не визначену територію поведінки. Тут кожна файлова система може бути різною, навіть від однієї версії ОС до іншої.)
Якщо ви виявите, що ви отримаєте помилкові позитивні результати, рекомендую перейти до цього find | cpio
варіанту у відповіді Жиля .
find somedir -type f -exec sh -c "openssl dgst -sha1 -binary {} | xxd -p" \; | sort | openssl dgst -sha1
ігнорувати всі назви файлів (слід працювати з новими рядками)
Контрольна сума повинна мати детерміноване та однозначне подання файлів як рядка. Детермінований означає, що якщо ви розмістите ті самі файли в одних і тих же місцях, ви отримаєте той самий результат. Однозначно означає, що два різних набори файлів мають різні подання.
Створення архіву, що містить файли, є хорошим початком. Це однозначне подання (очевидно, оскільки ви можете відновити файли, витягнувши архів). Він може включати метадані файлів, такі як дати та права власності. Однак це ще не зовсім правильно: архів неоднозначний, оскільки його подання залежить від порядку, в якому зберігаються файли, та, якщо застосовно, від стиснення.
Рішенням є сортування імен файлів перед їх архівуванням. Якщо назви ваших файлів не містять нових рядків, ви можете запустити їх 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()
LC_ALL=C sort
перевірки з різних середовищ ... (+ 1 btw)
LC_ALL=C
є важливим, якщо він працює на декількох машинах та ОС.
cpio -o -
означає? Чи cpio не використовує stdin / out за замовчуванням? GNU cpio 2.12 виробляєcpio: Too many arguments
Погляньте на md5deep . Деякі функції md5deep, які можуть вас зацікавити:
Рекурсивна операція - md5deep здатний рекурсивно вивчити ціле дерево каталогів. Тобто обчисліть MD5 для кожного файлу в каталозі та для кожного файла у кожному підкаталозі.
Режим порівняння - md5deep може прийняти список відомих хешів і порівняти їх із набором вхідних файлів. Програма може відображати або ті вхідні файли, які відповідають списку відомих хешів, або ті, які не відповідають.
...
.../foo: Is a directory
, що дає?
md5deep -r -l -j0 . | md5sum
(де -r
є рекурсивним, -l
означає "використовувати відносні шляхи", щоб абсолютний шлях файлів не втручався при спробі порівняння вмісту двох каталогів, і -j0
означає використовувати 1 потік для запобігання недетермінізму через до окремих md5sums, що повертаються в різних порядках).
Якщо ваша мета полягає лише в тому, щоб знайти відмінності між двома каталогами, подумайте про використання diff.
Спробуйте це:
diff -qr dir1 dir2
Ви можете хешувати кожен файл рекурсивно, а потім хешувати отриманий текст:
> md5deep -r -l . | sort | md5sum
d43417958e47758c6405b5098f151074 *-
md5deep потрібно.
md5deep
використання hashdeep
на ubuntu 16.04, оскільки пакет md5deep - це лише перехідний макет для hashdeep.
## Invoked from: /home/myuser/dev/
який ваш поточний шлях і ## $ hashdeep -s -r -l ~/folder/
. Це вдалося сортувати, тож остаточний хеш буде іншим, якщо ви зміните поточну папку чи командний рядок.
Мені потрібна була версія, яка перевіряла лише імена файлів, оскільки вміст знаходиться в різних каталогах.
Ця версія (відповідь Уоррена Янга) дуже допомогла, але моя версія md5sum
виводить ім'я файлу (відносно шляху, з якого я запустив команду), і назви папок були різними, тому, хоч окремі контрольні суми файлів відповідали, остаточна контрольна сума не зробила 'т.
Щоб виправити це, у моєму випадку мені просто потрібно було викреслити ім’я файлу з кожного рядка find
виводу (виберіть лише перше слово як розділене пробілами cut
):
find -s somedir -type f -exec md5sum {} \; | cut -d" " -f1 | md5sum
рішення :
$ pip install checksumdir
$ checksumdir -a md5 assets/js
981ac0bc890de594a9f2f40e00f13872
$ checksumdir -a sha1 assets/js
88cd20f115e31a1e1ae381f7291d0c8cd3b92fad
працює швидше і простіше рішення, ніж баш сценаріїв.
див. документ: https://pypi.python.org/pypi/checksumdir/1.0.5
nix-hash
від менеджера пакетів Nix
Команда nix-хеш обчислює криптографічний хеш вмісту кожного шляху і друкує його на стандартному виході. За замовчуванням він обчислює хеш MD5, але доступні й інші хеш-алгоритми. Хеш друкується шістнадцяткою.
Хеш обчислюється через серіалізацію кожного контуру: дамп дерева файлової системи, корінням якого є шлях. Це дозволяє хеджувати каталоги та посилання, а також звичайні файли. Демпп у форматі NAR, створеному nix-store --dump. Таким чином, nix-хеш-шлях дає той самий криптографічний хеш, що і nix-store --dump path | md5sum.
Я використовую цей мій фрагмент для помірних обсягів :
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 -
-xdev
прапор?
man find
та прочитати цю чудову інструкцію;)
-xdev Don't descend directories on other filesystems.
Гарна чекова сума дерев - це ідентифікатор дерева Git.
На жаль, не існує автономного інструменту, який міг би це зробити (принаймні, я цього не знаю), але якщо у вас Git під рукою, ви можете просто зробити вигляд, щоб створити нове сховище та додати до індексу файли, які ви хочете перевірити.
Це дозволяє створити (відтворюваний) хеш дерева - який включає лише вміст, імена файлів та деякі скорочені режими файлів (виконувані файли).
Як подальший результат до цієї чудової відповіді , якщо ви хочете прискорити обчислення контрольної суми для великого каталогу, спробуйте GNU Parallel :
find -s somedir -type f | parallel -k -n 100 md5 {} | md5
(Для цього використовується Mac з md5
, замініть за потребою.)
-k
Прапор важливо, що наказує parallel
підтримувати порядок, в іншому випадку загальна сума може змінитися , щоб запустити працювати , навіть якщо ці файли все одно. -n 100
каже, щоб запустити кожен екземпляр md5
зі 100 аргументів, це параметр, який можна налаштувати для найкращого часу виконання. Дивіться також -X
прапор parallel
(хоча в моєму особистому випадку спричинив помилку.)
Сценарій, який добре перевірений і підтримує ряд операцій, включаючи пошук дублікатів, порівняння даних і метаданих, показ доповнень, а також зміни та видалення, вам може сподобатися Fingerprint .
Відбиток пальців зараз не створює єдиної контрольної суми для каталогу, але файл стенограми, який включає контрольні суми для всіх файлів у цьому каталозі.
fingerprint analyze
Це генерується index.fingerprint
в поточному каталозі, який включає контрольні суми, назви файлів та розміри файлів. За замовчуванням він використовує і MD5
та, і SHA1.256
.
В майбутньому я сподіваюся додати підтримку Merkle Trees у Fingerprint, що дасть вам єдину контрольну суму верхнього рівня. Зараз вам потрібно зберегти цей файл для перевірки.
Я не хотів нових виконуваних файлів, а також незграбних рішень, тож ось мій:
#!/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
Це те, що я маю на голові, кожен, хто витратив деякий час на це, практично зловив би інші випадки та кутові справи.
Ось інструмент (відмова від відповідальності: я дописую це) 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
Робиться індивідуально для всіх файлів у кожному каталозі.
# Calculating
find dir1 | xargs md5sum > dir1.md5
find dir2 | xargs md5sum > dir2.md5
# Comparing (and showing the difference)
paste <(sort -k2 dir1.md5) <(sort -k2 dir2.md5) | awk '$1 != $3'
Ця відповідь має бути доповненням до підходу використання виводу Tar для хешування вмісту каталогів, як це було запропоновано (серед іншого) у чудових відповідях Уоррена Янга та Гілла деякий час тому.
З цього моменту принаймні openSUSE (з моменту випуску 12.2) змінив стандартний формат GNU Tar з "GNU tar 1.13.x формат" на (трохи) вищий "POSIX 1003.1-2001 (pax) формат" . Також вгору за течією (серед розробників GNU Tar) вони обговорюють виконання тієї ж міграції, див., Наприклад, останній параграф на цій сторінці посібника з GNU Tar :
Формат за замовчуванням для GNU tar визначається під час компіляції. Ви можете перевірити це, запустивши
tar --help
та вивчивши останні рядки його виводу. Зазвичай GNU tar призначений для створення архівів уgnu
форматі, однак майбутня версія перейде наposix
.
(Ця сторінка також дає хороший огляд різних архівних форматів, доступних у GNU Tar.)
У нашому випадку, коли ми орієнтуємося на вміст каталогів і хешуємо результат, і не вживаючи конкретних заходів, перехід від формату GNU до POSIX має такі наслідки:
Незважаючи на однаковий вміст каталогу, отримана контрольна сума буде різною.
Незважаючи на однаковий вміст каталогу, отримана контрольна сума буде відрізнятися від запуску до запуску, якщо використовуються заголовки pax за замовчуванням.
Останнє випливає з того, що формат POSIX (pax) включає розширені заголовки pax, які визначаються рядком формату, який за замовчуванням визначається %d/PaxHeaders.%p/%f
у GNU Tar. У цьому рядку специфікатор %p
замінюється ідентифікатором процесу генеруючого процесу Tar, який, звичайно, відрізняється від запуску до запуску. Дивіться цей розділ в керівництві GNU Tar і , зокрема , цей для деталей.
Щойно зараз, починаючи з 2019-03-28, існує прийняте зобов’язання, що усуває це питання.
Отже, щоб мати можливість продовжувати використовувати GNU Tar в даному випадку використання, я можу порекомендувати наступні альтернативні варіанти:
Використовуйте опцію Tar, --format=gnu
щоб явно сказати Tar, щоб створити архів у "старому" форматі. Це обов'язково для підтвердження "старих" контрольних сум.
Використовуйте новіший формат POSIX, але явно вкажіть відповідний заголовок pax, наприклад, by --pax-option="exthdr.name=%d/PaxHeaders/%f"
. Однак це порушує відсталу сумісність із "старими" контрольними сумами.
Ось фрагмент коду Bash, який я регулярно використовую для обчислення контрольних сум вмісту каталогів, включаючи метадані:
( export LC_ALL=C
find <paths> ! -type s -print0 |
sort -z |
tar cp --format=gnu --numeric-owner \
--atime-preserve \
--no-recursion --null --files-from - |
md5sum --binary; )
Тут <paths>
замінено пробілом розділений список шляхів усіх каталогів, які я хочу охопити контрольною сумою. Мета використання локальної мови C, розділення нульових байтів імен файлів і використання знаходження та сортування для отримання незалежного порядку файлової системи файлів в архіві вже достатньо обговорюється в інших відповідях.
Навколишні круглі дужки зберігають LC_ALL
налаштування місцевого значення в підшарці.
Крім того, я використовую вираз ! -type s
з , find
щоб уникнути попереджень Tar , які виникають , якщо сокет файли є частиною вмісту каталогу: GNU Tar НЕ архівує сокети. Якщо ви хочете отримувати сповіщення про пропущені розетки, залиште це вираження подалі.
Я використовую --numeric-owner
разом з Tar, щоб пізніше перевірити контрольні суми навіть у системах, де не всі власники файлів відомі.
--atime-preserve
Варіант для Tar краще опустити , якщо якийсь - або з <paths>
лежиш на змонтоване пристрої тільки для читання. Інакше вас попередить про кожен окремий файл, часова мітка доступу Tar не вдалося відновити. Для ввімкнення запису <paths>
я використовую цю опцію, добре, щоб зберегти часові позначки доступу в каталогах хешованих файлів.
Варіант Tar --no-recursion
, який уже використовувався в пропозиції Джилла , не дозволяє рекурсивно спускатися в каталоги сам по собі, а працювати замість нього файл по файлу на будь-якому, що його подають з відсортованого find
виводу.
І, нарешті, неправда, якою я користуюсь md5sum
: я насправді використовую sha256sum
.
Якщо вам не потрібен md5, ви можете спробувати
find . -type f | xargs cksum | cksum
find .
йогоfind somedir
. Таким чином імена файлів однакові при наданні різних специфікацій шляху; це може бути складним :-)