Jak mogę obliczyć sumę kontrolną MD5 katalogu?


133

Muszę obliczyć sumaryczną sumę kontrolną md5 dla wszystkich plików określonego typu ( *.pyna przykład) umieszczonych w katalogu i wszystkich podkatalogach.

Jaki jest najlepszy sposób, aby to zrobić?

Edycja: Proponowane rozwiązania są bardzo ładne, ale nie jest to dokładnie to, czego potrzebuję. Szukam rozwiązania pozwalającego uzyskać pojedynczą sumę kontrolną podsumowania, która jednoznacznie zidentyfikuje katalog jako całość - w tym zawartość wszystkich jego podkatalogów.


Spójrz na to i to, aby uzyskać bardziej szczegółowe wyjaśnienie.
luvieere

3
Wydaje mi się, że to pytanie superużytkownika.
Noldorin

8
Zwróć uwagę, że sumy kontrolne niczego jednoznacznie nie identyfikują.
Hosam Aly

1
Dlaczego miałbyś mieć dwa drzewa katalogów, które mogą być „takie same” lub nie, które chcesz jednoznacznie zidentyfikować? Czy czas tworzenia / modyfikacji / dostępu do pliku ma znaczenie? Czy kontrola wersji jest tym, czego naprawdę potrzebujesz?
jmucchiello

W moim przypadku naprawdę istotne jest podobieństwo zawartości całego drzewa katalogów, co oznacza AFAIK: 1) zawartość żadnego pliku w drzewie katalogów nie została zmieniona 2) nie dodano nowego pliku do drzewa katalogów 3) brak pliku został usunięty
victorz

Odpowiedzi:


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

Polecenie find wyświetla listę wszystkich plików kończących się na .py. Suma md5 jest obliczana dla każdego pliku .py. awk jest używany do wybierania sum md5 (ignorując nazwy plików, które mogą nie być unikalne). Sumy md5 są sortowane. Następnie zwracana jest suma md5 tej posortowanej listy.

Przetestowałem to, kopiując katalog testowy:

rsync -a ~/pybin/ ~/pybin2/

Zmieniłem nazwy niektórych plików w ~ / pybin2.

find...md5sumPolecenie zwraca taki sam wynik dla obu katalogach.

2bcf49a4d19ef9abd284311108d626f1  -

24
Zauważ, że ta sama suma kontrolna zostanie wygenerowana, jeśli nazwa pliku zostanie zmieniona. Więc to naprawdę nie pasuje do "sumy kontrolnej, która jednoznacznie identyfikuje katalog jako całość", jeśli weźmiesz pod uwagę układ pliku jako część podpisu.
Valentin Milea

1
możesz nieznacznie zmienić wiersz poleceń, aby poprzedzić każdą sumę kontrolną pliku nazwą pliku (lub nawet lepiej, względną ścieżką pliku z / ścieżka / do / katalog /), aby była ona brana pod uwagę w końcowej sumie kontrolnej.
Michael Zilbermann

4
@ zim2001: Tak, można to zmienić, ale jak zrozumiałem problem (zwłaszcza ze względu na komentarz OP pod pytaniem), OP chciał, aby dowolne dwa katalogi były traktowane jako równe, jeśli zawartość plików była identyczna niezależnie od nazwy pliku lub nawet ścieżka względna.
unutbu

@unutbu: Wiem; reagowałem na poprzednią notatkę od Valentina Milea.
Michael Zilbermann

@ValentinMilea po prostu usuń awk ...część, jeśli weźmiesz pod uwagę część układu podpisu.
segfault

166

Utwórz plik archiwum tar w locie i potokuj go do md5sum:

tar c dir | md5sum

Daje to pojedynczą sumę md5, która powinna być unikalna dla konfiguracji pliku i podkatalogu. Na dysku nie są tworzone żadne pliki.


25
@CharlesB z pojedynczą sumą kontrolną nigdy nie wiesz, który plik jest inny. Pytanie dotyczyło pojedynczej sumy kontrolnej dla katalogu.
Hawken

17
ls -alR dir | md5sum. To jest jeszcze lepsze bez kompresji, tylko odczyt. Jest wyjątkowy, ponieważ zawartość zawiera czas modyfikacji i rozmiar pliku;)
Sid

14
@ Daps0l - w moim poleceniu nie ma kompresji. Musisz dodać zdla gzip lub jbzip2. Ja też nie.
ire_and_curses

7
Uważaj, aby w ten sposób zintegrować sygnaturę czasową plików i inne rzeczy w obliczeniu sumy kontrolnej, a nie tylko zawartość plików
Michael Zilbermann

10
To jest urocze, ale tak naprawdę nie działa. Nie ma gwarancji, że tardwukrotne wykonanie tego samego zestawu plików lub na dwóch różnych komputerach da dokładnie ten sam wynik.
fletom

46

Sugestia ire_and_curses dotycząca używania tar c <dir>ma pewne problemy:

  • tar przetwarza pozycje katalogu w kolejności, w jakiej są one przechowywane w systemie plików i nie ma możliwości zmiany tej kolejności. Skutecznie może to dać zupełnie inne wyniki, jeśli masz „ten sam” katalog w różnych miejscach, a ja nie wiem jak to naprawić (tar nie może „sortować” swoich plików wejściowych w określonej kolejności).
  • Zwykle interesuje mnie, czy numery groupid i ownerid są takie same, niekoniecznie czy ciąg reprezentujący grupę / właściciela jest taki sam. Jest to zgodne z tym, co na przykład rsync -a --deleterobi: synchronizuje praktycznie wszystko (bez xattrs i acls), ale synchronizuje właściciela i grupę na podstawie ich identyfikatora, a nie reprezentacji ciągu. Więc jeśli zsynchronizowałeś się z innym systemem, który niekoniecznie ma tych samych użytkowników / grupy, powinieneś dodać --numeric-ownerflagę do tar
  • tar będzie zawierał nazwę pliku katalogu, który sam sprawdzasz, tylko coś, o czym należy pamiętać.

Dopóki nie ma rozwiązania dla pierwszego problemu (lub jeśli nie masz pewności, że nie dotyczy on Ciebie), nie zastosowałbym tego podejścia.

W findoparciu rozwiązania zaproponowane powyżej to również nie jest dobre, ponieważ obejmują one tylko te pliki, nie katalogów, która staje się problemem, jeśli was sum kontrolnych należy pamiętać pustych katalogów.

Wreszcie, większość sugerowanych rozwiązań nie sortuje się konsekwentnie, ponieważ sortowanie może być różne w różnych systemach.

Oto rozwiązanie, które wymyśliłem:

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

Uwagi dotyczące tego rozwiązania:

  • Ma LC_ALL=Cto na celu zapewnienie niezawodnej kolejności sortowania w różnych systemach
  • Nie powoduje to rozróżnienia między katalogiem „o nazwie \ nzawiniętej linii” a dwoma katalogami o nazwie „o nazwie” i „z nową linią”, ale prawdopodobieństwo wystąpienia tego jest bardzo mało prawdopodobne. Zwykle naprawia się to za pomocą -print0flagi dla, findale ponieważ są tu inne rzeczy, widzę tylko rozwiązania, które uczyniłyby polecenie bardziej skomplikowanym, niż jest warte.

PS: jeden z moich systemów używa ograniczonego busyboxa, findktóry nie obsługuje -execani -print0flag, a także dodaje „/” w celu oznaczenia katalogów, podczas gdy findutils find nie wydaje się, więc dla tego komputera muszę uruchomić:

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

Na szczęście nie mam plików / katalogów z nowymi wierszami w nazwach, więc nie jest to problem w tym systemie.


1
+1: Bardzo interesujące! Czy chcesz powiedzieć, że kolejność może się różnić między różnymi typami systemów plików lub w ramach tego samego systemu plików?
ire_and_curses

2
obie. zależy to tylko od kolejności wpisów w każdym katalogu. Wpisy katalogów AFAIK (w systemie plików) są właśnie tworzone w kolejności, w której „tworzysz pliki w katalogu”. Prosty przykład: $ mkdir a; dotknij / plik-1; dotknij a / file-2 $ mkdir b; dotknij b / plik-2; touch b / file-1 $ (cd a; tar -c. | md5sum) fb29e7af140aeea5a2647974f7cdec77 - $ (cd b; tar -c. | md5sum) a3a39358158a87059b9f111ccffa1023 -
Dieter_be

14

Jeśli zależy Ci tylko na plikach, a nie na pustych katalogach, działa to ładnie:

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

10

Ze względu na kompletność istnieje md5deep (1) ; nie ma bezpośredniego zastosowania ze względu na wymagania dotyczące filtra * .py, ale powinno działać dobrze razem z find (1).


Jakich parametrów użyłbym, gdybym chciał tylko obliczyć sumę kontrolną md5 katalogu?
Gabriel Fair

9

Rozwiązanie, które sprawdziło się najlepiej dla mnie:

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

Powód, dla którego to działało najlepiej dla mnie:

  1. obsługuje nazwy plików zawierające spacje
  2. Ignoruje metadane systemu plików
  3. Wykrywa, czy nazwa pliku została zmieniona

Problemy z innymi odpowiedziami:

Metadane systemu plików nie są ignorowane w przypadku:

tar c - "$path" | md5sum

Nie obsługuje nazw plików zawierających spacje ani nie wykrywa zmiany nazwy pliku:

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

4

Jeśli chcesz, aby jedna suma md5 obejmowała cały katalog, zrobiłbym coś takiego

cat *.py | md5sum 

1
W przypadku podkatalogów użyj czegoś takiego jak cat **.py| md5sum
Ramon

3

Suma kontrolna wszystkich plików, w tym zarówno zawartość, jak i ich nazwy

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

To samo co powyżej, ale zawiera tylko pliki * .py

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

Jeśli chcesz, możesz również śledzić linki symboliczne

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

Inne opcje, które możesz rozważyć w połączeniu z 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

Technicznie wystarczy biec ls -lR *.py | md5sum. O ile nie martwisz się, że ktoś zmodyfikuje pliki i przywróci ich pierwotne daty i nigdy nie zmieni rozmiarów plików, dane wyjściowe z lspowinny informować, czy plik się zmienił. Mój unix-foo jest słaby, więc możesz potrzebować więcej parametrów wiersza poleceń, aby uzyskać czas utworzenia i czas modyfikacji do wydrukowania. lspoinformuje Cię również, czy uprawnienia do plików uległy zmianie (i jestem pewien, że są przełączniki, które mogą to wyłączyć, jeśli nie dbasz o to).


3
Może to pasować do niektórych przypadków użycia, ale generalnie chciałbyś, aby suma kontrolna odzwierciedlała tylko zawartość, a nie daty. Na przykład, jeśli mam touchplik, aby zmienić jego datę (ale nie jego zawartość), spodziewałbym się, że suma kontrolna pozostanie niezmieniona.
Todd Owen


1

Miałem ten sam problem, więc wymyśliłem ten skrypt, który po prostu wyświetla sumy md5 plików w katalogu i jeśli znajdzie podkatalog, uruchamia się ponownie stamtąd, aby to się stało, skrypt musi być w stanie uruchomić bieżący katalogu lub z podkatalogu, jeśli wspomniany argument jest przekazywany w $ 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

Jestem prawie pewien, że ten skrypt nie powiedzie się, jeśli nazwy plików zawierają spacje lub cudzysłowy. Uważam to za denerwujące przy skryptach basha, ale zmieniam IFS.
localhost

1

Jeśli chcesz naprawdę uniezależnić się od atrybutów systemu plików i od różnic w poziomie bitów niektórych wersji tar, możesz użyć cpio:

cpio -i -e theDirname | md5sum

0

Istnieją jeszcze dwa rozwiązania:

Stwórz:

du -csxb /path | md5sum > file

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

Czek:

du -csxb /path | md5sum -c file

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

0

md5sumdziałało dobrze, ale miałem problemy z sortsortowaniem nazw plików. Więc zamiast tego posortowałem według md5sumwyniku. Musiałem również wykluczyć niektóre pliki, aby uzyskać porównywalne wyniki.

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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.