Linux: obliczyć pojedynczy skrót dla danego folderu i zawartości?

98

Z pewnością musi istnieć sposób, aby to łatwo zrobić!

Wypróbowałem aplikacje wiersza poleceń systemu Linux, takie jak sha1sumi, md5sumale wydaje się, że są w stanie obliczyć skróty poszczególnych plików i wyprowadzić listę wartości skrótu, po jednej dla każdego pliku.

Muszę wygenerować jeden skrót dla całej zawartości folderu (nie tylko nazw plików).

Chciałbym zrobić coś takiego

sha1sum /folder/of/stuff > singlehashvalue

Edycja: aby wyjaśnić, moje pliki znajdują się na wielu poziomach w drzewie katalogów, nie wszystkie znajdują się w tym samym folderze głównym.

Ben L.
źródło
1
Czy przez „całą zawartość” masz na myśli dane logiczne wszystkich plików w katalogu lub jego dane wraz z meta podczas docierania do głównego skrótu? Ponieważ kryteria wyboru twojego przypadku użycia są dość szerokie, w mojej odpowiedzi starałem się odnieść do kilku praktycznych.
Six-k

Odpowiedzi:

124

Jednym z możliwych sposobów byłoby:

sha1sum ścieżka / do / folderu / * | sha1sum

Jeśli istnieje całe drzewo katalogów, prawdopodobnie lepiej będzie użyć funkcji find i xargs. Jednym z możliwych poleceń byłoby

znajdź ścieżkę / do / folderu -type f -print0 | sort -z | xargs -0 suma sha1 | sha1sum

I na koniec, jeśli musisz również wziąć pod uwagę uprawnienia i puste katalogi:

(find path/to/folder -type f -print0  | sort -z | xargs -0 sha1sum;
 find path/to/folder \( -type f -o -type d \) -print0 | sort -z | \
   xargs -0 stat -c '%n %a') \
| sha1sum

Argumenty do statspowodują, że wypisze nazwę pliku, po której następują ósemkowe uprawnienia. Te dwa znalezienia zostaną uruchomione jedno po drugim, powodując podwojenie ilości operacji we / wy dysku, pierwsze wyszukując wszystkie nazwy plików i sumując zawartość, a drugie znajdując wszystkie nazwy plików i katalogów, drukując nazwę i tryb. Lista „nazw plików i sum kontrolnych”, po której następują „nazwy i katalogi z uprawnieniami”, zostanie sprawdzona jako suma kontrolna, aby uzyskać mniejszą sumę kontrolną.

Watyna
źródło
2
i nie zapomnij ustawić LC_ALL = POSIX, aby różne narzędzia tworzyły wyjście niezależne od lokalizacji.
David Schmitt,
2
Znalazłem kota | sha1sum jest znacznie szybszy niż sha1sum | sha1sum. YMMV, wypróbuj każdy z nich w swoim systemie: czas znajdź ścieżkę / do / folderu -type f -print0 | sort -z | xargs -0 suma sha1 | sha1sum; czas znajdź ścieżkę / do / folderu -type f -print0 | sort -z | xargs -0 kot | sha1sum
Bruno Bronosky
5
@RichardBronosky - Załóżmy, że mamy dwa pliki, A i B. A zawiera „foo”, a B zawiera „bar był tutaj”. Waszą metodą nie bylibyśmy w stanie oddzielić tego od dwóch plików C i D, gdzie C zawiera „foobar”, a D zawiera „było tutaj”. Haszując każdy plik osobno, a następnie łącząc wszystkie pary „hash nazw plików”, możemy zobaczyć różnicę.
Vatine,
2
Aby to działało niezależnie od ścieżki katalogu (tj. Gdy chcesz porównać skróty dwóch różnych folderów), musisz użyć ścieżki względnej i przejść do odpowiedniego katalogu, ponieważ ścieżki są zawarte w końcowym haszu:find ./folder -type f -print0 | sort -z | xargs -0 sha1sum | sha1sum
robbles
3
@robbles To prawda i dlaczego nie umieściłem inicjału /na wędzidle path/to/folder.
Watyna
26
  • Użyj narzędzia do wykrywania włamań do systemu plików, takiego jak aide .

  • haszuj kulkę tar z katalogu:

    tar cvf - /path/to/folder | sha1sum

  • Zakoduj coś samemu, na przykład oneliner Vatine :

    find /path/to/folder -type f -print0 | sort -z | xargs -0 sha1sum | sha1sum

David Schmitt
źródło
3
+1 za roztwór smoły. To jest najszybsze, ale porzucenie b. Gadatliwości tylko je spowalnia.
Bruno Bronosky,
7
zwróć uwagę, że rozwiązanie tar zakłada, że ​​pliki są w tej samej kolejności, gdy je porównujesz. To, czy są, zależy od systemu plików, w którym znajdują się pliki podczas porównywania.
nr
5
Skrót git nie jest odpowiedni do tego celu, ponieważ zawartość pliku jest tylko częścią jego danych wejściowych. Nawet w przypadku początkowego zatwierdzenia gałęzi, na wartość skrótu wpływa komunikat o zatwierdzeniu i metadane zatwierdzenia, takie jak czas zatwierdzenia. Jeśli wielokrotnie zatwierdzisz tę samą strukturę katalogów, za każdym razem otrzymasz inny hash, a zatem wynikowy hash nie jest odpowiedni do określenia, czy dwa katalogi są dokładnymi kopiami siebie nawzajem, tylko przesyłając hash.
Zoltan
1
@Zoltan hasz git jest całkowicie w porządku, jeśli używasz hasha drzewa, a nie skrótu zatwierdzenia.
hobbs
1
@hobbs Pierwotnie odpowiedź brzmiała „commit hash”, co z pewnością nie nadaje się do tego celu. Hash drzewa brzmi jak znacznie lepszy kandydat, ale nadal mogą istnieć ukryte pułapki. Przychodzi mi do głowy, że ustawienie bitu wykonywalnego w niektórych plikach zmienia skrót drzewa. Musisz wydać git config --local core.fileMode falseprzed zobowiązaniem, aby tego uniknąć. Nie wiem, czy jest więcej takich zastrzeżeń.
Zoltan
14

Możesz to zrobić tar -c /path/to/folder | sha1sum

S.Lott
źródło
17
Jeśli chcesz powielić tę sumę kontrolną na innym komputerze, tar może nie być dobrym wyborem, ponieważ format wydaje się mieć miejsce na niejednoznaczność i istnieje w wielu wersjach, więc tar na innym komputerze może generować inne dane wyjściowe z tych samych plików.
slowdog
2
ważne kwestie slowdog za Niezależnie od tego, czy dbasz o zawartości plików, prawa, itp, ale nie modyfikacja czasu, można dodać --mtimeopcję tak: tar -c /path/to/folder --mtime="1970-01-01" | sha1sum.
Plik binarny
@ S.Lott, jeśli rozmiar katalogu jest duży, mam na myśli, jeśli rozmiar katalogu jest tak duży, spakowanie go i pobranie md5 zajmie więcej czasu
Kasun Siyambalapitiya
13

Jeśli chcesz tylko sprawdzić, czy coś w folderze się zmieniło, polecam ten:

ls -alR --full-time /folder/of/stuff | sha1sum

Po prostu da ci hash wyjścia ls, który zawiera foldery, podfoldery, ich pliki, ich znacznik czasu, rozmiar i uprawnienia. Prawie wszystko, czego potrzebujesz, aby ustalić, czy coś się zmieniło.

Zwróć uwagę, że to polecenie nie wygeneruje skrótu dla każdego pliku, ale dlatego powinno być szybsze niż użycie find.

Shumoapp
źródło
1
Nie jestem pewien, dlaczego nie ma więcej głosów pozytywnych, biorąc pod uwagę prostotę rozwiązania. Czy ktoś może wyjaśnić, dlaczego to nie działałoby dobrze?
Dave C
1
Przypuszczam, że to nie jest idealne, ponieważ wygenerowany hash będzie oparty na właścicielu pliku, konfiguracji formatu daty itp.
Ryota
1
Polecenie ls można dostosować, aby wyświetlało cokolwiek chcesz. Możesz zamienić -l na -gG, aby pominąć grupę i właściciela. Możesz zmienić format daty za pomocą opcji --time-style. Zasadniczo sprawdź stronę podręcznika ls i zobacz, co odpowiada Twoim potrzebom.
Shumoapp
@DaveC Ponieważ jest to prawie bezużyteczne. Jeśli chcesz porównać nazwy plików, po prostu porównaj je bezpośrednio. Nie są tak duże.
Navin
7
@Navin Z pytania nie jest jasne, czy konieczne jest haszowanie zawartości pliku, czy też wykrycie zmiany w drzewie. Każdy przypadek ma swoje zastosowania. Na przykład przechowywanie nazw plików 45K w drzewie jądra jest mniej praktyczne niż pojedynczy hash. ls -lAgGR --block-size = 1 --time-style = +% s | sha1sum działa świetnie dla mnie
yashma
5

Solidne i czyste podejście

  • Po pierwsze, nie marnuj dostępnej pamięci ! Zamiast podawać cały plik, należy haszować plik fragmentami.
  • Różne podejścia do różnych potrzeb / celów (wszystkie z poniższych lub wybierz to, co ma zastosowanie):
    • Hashuj tylko nazwy wszystkich pozycji w drzewie katalogów
    • Haszuj zawartość pliku wszystkich wpisów (pozostawiając meta jak, numer i-węzła, ctime, atime, mtime, rozmiar itp., Masz pomysł)
    • W przypadku linku symbolicznego jego zawartością jest nazwa odniesienia. Haszuj lub wybierz pominięcie
    • Śledź lub nie śledź (rozwiązana nazwa) dowiązania symbolicznego podczas haszowania zawartości wpisu
    • Jeśli jest to katalog, jego zawartość to tylko wpisy katalogu. Podczas przechodzenia rekurencyjnego zostaną one ostatecznie zaszyfrowane, ale czy nazwy pozycji katalogów tego poziomu powinny być zaszyfrowane, aby oznaczyć ten katalog? Pomocne w przypadkach użycia, w których hash jest wymagany do szybkiego zidentyfikowania zmiany bez konieczności głębokiego przemierzania zawartości. Przykładem może być zmiana nazwy pliku, ale reszta zawartości pozostaje taka sama i wszystkie są dość dużymi plikami
    • Dobrze obsługuj duże pliki (ponownie pamiętaj o pamięci RAM)
    • Obsługuj bardzo głębokie drzewa katalogów (pamiętaj o deskryptorach otwartych plików)
    • Obsługuj niestandardowe nazwy plików
    • Jak postępować z plikami, które są gniazdami, potokami / FIFO, urządzeniami blokowymi, urządzeniami char? Czy też trzeba je mieszać?
    • Nie aktualizuj czasu dostępu do żadnego wpisu podczas przemierzania, ponieważ będzie to efekt uboczny i przyniesie efekt przeciwny do zamierzonego (intuicyjny?) W niektórych przypadkach użycia.

To jest to, co mam na głowie, każdy, kto spędził trochę czasu nad tym, praktycznie złapałby inne pułapki i przypadki narożne.

Oto narzędzie , bardzo mało pamięci, które dotyczy większości przypadków, może być nieco szorstkie na krawędziach, ale było całkiem pomocne.

Przykładowe użycie i dane wyjściowe dtreetrawl.

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

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

Application Options:
  -t, --terse               Produce a terse output; parsable.
  -j, --json                Output as JSON
  -d, --delim=:             Character or string delimiter/separator for terse output(default ':')
  -l, --max-level=N         Do not traverse tree beyond N level(s)
  --hash                    Enable hashing(default is MD5).
  -c, --checksum=md5        Valid hashing algorithms: md5, sha1, sha256, sha512.
  -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
  -s, --hash-symlink        Include symbolic links' referent name while calculating the root checksum
  -e, --hash-dirent         Include hash of directory entries while calculating root checksum

Fragment przyjaznego dla człowieka wyniku:

...
... //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
sześć-k
źródło
1
Czy możesz podać krótki przykład, jak uzyskać solidny i czysty sha256 folderu, może dla folderu Windows z trzema podkatalogami i kilkoma plikami w każdym?
Ferit
3

Jeśli chcesz po prostu zaszyfrować zawartość plików, ignorując nazwy plików, możesz użyć

cat $FILES | md5sum

Upewnij się, że masz pliki w tej samej kolejności podczas obliczania skrótu:

cat $(echo $FILES | sort) | md5sum

Ale nie możesz mieć katalogów na liście plików.


źródło
2
Przeniesienie końca jednego pliku na początek pliku, który następuje po nim w kolejności alfabetycznej, nie wpłynie na skrót, ale powinno. W skrócie musiałby być zawarty separator pliku lub długości plików.
Jason Stangroome
3

Kolejne narzędzie do osiągnięcia tego:

http://md5deep.sourceforge.net/

Jak to brzmi: jak md5sum, ale także rekurencyjne, plus inne funkcje.

Jacek
źródło
1
Chociaż ten link może odpowiedzieć na pytanie, lepiej jest zawrzeć tutaj zasadnicze części odpowiedzi i podać link do odniesienia. Odpowiedzi zawierające tylko łącze mogą stać się nieprawidłowe, jeśli połączona strona ulegnie zmianie.
Mamoun Benghezal
3

Jeśli to jest repozytorium git i chcesz zignorować wszystkie pliki w programie .gitignore, możesz użyć tego:

git ls-files <your_directory> | xargs sha256sum | cut -d" " -f1 | sha256sum | cut -d" " -f1

To działa dobrze dla mnie.

ndbroadbent
źródło
Wielkie dzięki! :)
visortelle
W przypadku wielu zastosowań to podejście jest lepsze. Haszowanie tylko plików z kodem źródłowym powoduje uzyskanie wystarczająco unikalnego skrótu w znacznie krótszym czasie.
John McGehee,
2

Musiałem sprawdzić cały katalog w poszukiwaniu zmian plików.

Ale z wykluczaniem, sygnaturami czasowymi, właścicielami katalogów.

Celem jest uzyskanie wszędzie identycznej sumy, jeśli pliki są identyczne.

W tym hostowane na innych komputerach, niezależnie od wszystkiego oprócz plików lub zmian w nich.

md5sum * | md5sum | cut -d' ' -f1

Generuje listę skrótów według pliku, a następnie łączy te skróty w jeden.

Jest to o wiele szybsze niż metoda tar.

Aby zapewnić większą prywatność w naszych hashach , możemy użyć sumy sha512 w tej samej recepturze.

sha512sum * | sha512sum | cut -d' ' -f1

Hasze są również identyczne wszędzie przy użyciu sumy sha512, ale nie ma znanego sposobu, aby to odwrócić.

NVRM
źródło
Wydaje się to o wiele prostsze niż akceptowana odpowiedź na haszowanie katalogu. Nie stwierdziłem, że zaakceptowana odpowiedź jest wiarygodna. Jeden problem ... czy jest szansa, że ​​skróty mogą pojawić się w innej kolejności? sha256sum /tmp/thd-agent/* | sortto jest to, czego staram się w celu uzyskania niezawodnego zamówienia, a następnie po prostu to wszystko.
thinktt
Cześć, wygląda na to, że skróty są domyślnie uporządkowane alfabetycznie. Co masz na myśli mówiąc o rzetelnym zamawianiu? Musisz to wszystko zorganizować samodzielnie. Na przykład przy użyciu tablic asocjacyjnych wpis + hash. Następnie sortujesz tę tablicę według wpisu, co daje listę obliczonych skrótów w kolejności sortowania. Uważam, że w przeciwnym razie można użyć obiektu json i bezpośrednio zhaszować cały obiekt.
NVRM
Jeśli rozumiem, mówisz, że haszuje pliki w porządku alfabetycznym. Wydaje się słuszne. Coś w zaakceptowanej odpowiedzi powyżej dawało mi czasami różne rozkazy, więc staram się tylko upewnić, że to się nie powtórzy. Na koniec będę się trzymał sortowania. Wydaje się, że działa. Jedyny problem z tą metodą w porównaniu z zaakceptowaną odpowiedzią, który widzę, to nie dotyczy zagnieżdżonych folderów. W moim przypadku nie mam żadnych folderów, więc działa świetnie.
thinktt
o czym ls -r | sha256sum?
NVRM
@NVRM wypróbował to i sprawdził tylko zmiany nazwy pliku, a nie zawartości pliku
Gi0rgi0s
1

Spróbuj zrobić to w dwóch krokach:

  1. utwórz plik z hashami dla wszystkich plików w folderze
  2. hasz ten plik

Tak jak to:

# for FILE in `find /folder/of/stuff -type f | sort`; do sha1sum $FILE >> hashes; done
# sha1sum hashes

Lub zrób to wszystko na raz:

# cat `find /folder/of/stuff -type f | sort` | sha1sum
Joao da Silva
źródło
for F in 'find ...' ...nie działa, gdy masz spacje w nazwach (co zawsze robisz w dzisiejszych czasach).
mivk
1

Przepuściłbym wyniki dla poszczególnych plików sort(aby zapobiec zwykłej zmianie kolejności plików w celu zmiany skrótu) do md5sumlub sha1sum, cokolwiek wybierzesz.

Rafał Dowgird
źródło
1

Napisałem skrypt Groovy, aby to zrobić:

import java.security.MessageDigest

public static String generateDigest(File file, String digest, int paddedLength){
    MessageDigest md = MessageDigest.getInstance(digest)
    md.reset()
    def files = []
    def directories = []

    if(file.isDirectory()){
        file.eachFileRecurse(){sf ->
            if(sf.isFile()){
                files.add(sf)
            }
            else{
                directories.add(file.toURI().relativize(sf.toURI()).toString())
            }
        }
    }
    else if(file.isFile()){
        files.add(file)
    }

    files.sort({a, b -> return a.getAbsolutePath() <=> b.getAbsolutePath()})
    directories.sort()

    files.each(){f ->
        println file.toURI().relativize(f.toURI()).toString()
        f.withInputStream(){is ->
            byte[] buffer = new byte[8192]
            int read = 0
            while((read = is.read(buffer)) > 0){
                md.update(buffer, 0, read)
            }
        }
    }

    directories.each(){d ->
        println d
        md.update(d.getBytes())
    }

    byte[] digestBytes = md.digest()
    BigInteger bigInt = new BigInteger(1, digestBytes)
    return bigInt.toString(16).padLeft(paddedLength, '0')
}

println "\n${generateDigest(new File(args[0]), 'SHA-256', 64)}"

Możesz dostosować użycie, aby uniknąć drukowania każdego pliku, zmienić skrót wiadomości, usunąć mieszanie katalogów itp. Przetestowałem to z danymi testowymi NIST i działa zgodnie z oczekiwaniami. http://www.nsrl.nist.gov/testdata/

gary-macbook:Scripts garypaduana$ groovy dirHash.groovy /Users/garypaduana/.config
.DS_Store
configstore/bower-github.yml
configstore/insight-bower.json
configstore/update-notifier-bower.json
filezilla/filezilla.xml
filezilla/layout.xml
filezilla/lockfile
filezilla/queue.sqlite3
filezilla/recentservers.xml
filezilla/sitemanager.xml
gtk-2.0/gtkfilechooser.ini
a/
configstore/
filezilla/
gtk-2.0/
lftp/
menus/
menus/applications-merged/

79de5e583734ca40ff651a3d9a54d106b52e94f1f8c2cd7133ca3bbddc0c6758
haventchecked
źródło
0

Możesz sha1sumwygenerować listę wartości skrótu, a następnie sha1sumtę listę ponownie, zależy to od tego, co dokładnie chcesz osiągnąć.

Ronny Vindenes
źródło
0

Oto prosty, krótki wariant w Pythonie 3, który działa dobrze w przypadku małych plików (np. Drzewo źródłowe lub coś takiego, gdzie każdy plik z osobna można łatwo zmieścić w pamięci RAM), ignorując puste katalogi, w oparciu o pomysły z innych rozwiązań:

import os, hashlib

def hash_for_directory(path, hashfunc=hashlib.sha1):                                                                                            
    filenames = sorted(os.path.join(dp, fn) for dp, _, fns in os.walk(path) for fn in fns)         
    index = '\n'.join('{}={}'.format(os.path.relpath(fn, path), hashfunc(open(fn, 'rb').read()).hexdigest()) for fn in filenames)               
    return hashfunc(index.encode('utf-8')).hexdigest()                          

Działa to tak:

  1. Znajdź rekursywnie wszystkie pliki w katalogu i posortuj je według nazwy
  2. Oblicz hash (domyślnie: SHA-1) każdego pliku (wczytuje cały plik do pamięci)
  3. Utwórz indeks tekstowy z liniami „filename = hash”
  4. Zakoduj ten indeks z powrotem do ciągu bajtów UTF-8 i użyj skrótu

Możesz przekazać inną funkcję skrótu jako drugi parametr, jeśli SHA-1 nie jest twoją filiżanką herbaty.

Thomas Perl
źródło
0

Jak dotąd najszybszym sposobem jest nadal użycie smoły. Dzięki kilku dodatkowym parametrom możemy również pozbyć się różnicy spowodowanej przez metadane.

Aby użyć tar do haszowania katalogu, należy upewnić się, że sortujesz ścieżkę podczas tar, w przeciwnym razie zawsze jest inaczej.

tar -C <root-dir> -cf - --sort=name <dir> | sha256sum

ignoruj ​​czas

Jeśli nie zależy Ci na czasie dostępu lub modyfikacji czasu, użyj czegoś takiego jak, --mtime='UTC 2019-01-01' aby upewnić się, że wszystkie znaczniki czasu są takie same.

ignoruj ​​własność

Zwykle musimy dodać, --group=0 --owner=0 --numeric-owneraby ujednolicić metadane właściciela.

zignoruj ​​niektóre pliki

posługiwać się --exclude=PATTERN

Wang
źródło