webentwicklung-frage-antwort-db.com.de

Linux: Berechne einen einzelnen Hash für einen bestimmten Ordner und dessen Inhalt?

Sicher muss es einen Weg geben, dies einfach zu tun! 

Ich habe die Linux-Befehlszeilen-Apps wie sha1sum und md5sum ausprobiert, aber sie scheinen nur Hashes einzelner Dateien berechnen zu können und geben eine Liste von Hashwerten aus, eine für jede Datei. 

Ich muss einen einzelnen Hash für den gesamten Inhalt eines Ordners generieren (nicht nur für die Dateinamen).

Ich würde gerne so etwas machen

sha1sum /folder/of/stuff > singlehashvalue

Bearbeiten: Um zu verdeutlichen, dass sich meine Dateien auf mehreren Ebenen in einer Verzeichnisstruktur befinden, sitzen sie nicht alle im selben Stammordner.

63
Ben L

Ein möglicher Weg wäre:

 sha1sum Pfad/zu/Ordner/* | sha1sum 

Wenn es einen ganzen Verzeichnisbaum gibt, ist es wahrscheinlich besser, find und xargs zu verwenden. Ein möglicher Befehl wäre

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

Und schließlich, wenn Sie auch Berechtigungen und leere Verzeichnisse berücksichtigen müssen:

(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

Die Argumente für stat führen dazu, dass der Name der Datei gefolgt von den Oktalberechtigungen ausgegeben wird. Die beiden Suchvorgänge werden nacheinander ausgeführt, wodurch die Anzahl der Festplatten-E/A verdoppelt wird. Der erste findet alle Dateinamen und prüft den Inhalt, der zweite alle Datei- und Verzeichnisnamen, den Drucknamen und den Modus. Die Liste der "Dateinamen und Prüfsummen", gefolgt von "Namen und Verzeichnisse mit Berechtigungen", wird anschließend für eine kleinere Prüfsumme geprüft.

78
Vatine
  • Commit das Verzeichnis für git, benutze den Commit-Hash. Siehe metastore für eine Möglichkeit, Berechtigungen auch zu steuern.

  • Verwenden Sie ein Dateisystem-Angriffserkennungsprogramm wie aide .

  • hasch einen Tar Ball des Verzeichnisses:

    tar cvf - /path/to/folder | sha1sum

  • Code selbst, wie der Oneliner von vatin :

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

16
David Schmitt

Sie können tar -c /path/to/folder | sha1sum tun

8
S.Lott

Wenn Sie nur überprüfen möchten, ob sich etwas im Ordner geändert hat, würde ich Folgendes empfehlen:

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

Sie erhalten lediglich einen Hashwert der ls-Ausgabe, der Ordner, Unterordner, deren Dateien, Zeitstempel, Größe und Berechtigungen enthält. So ziemlich alles, was Sie benötigen, um festzustellen, ob sich etwas geändert hat. 

Bitte beachten Sie, dass dieser Befehl keinen Hash für jede Datei generiert. Aus diesem Grund sollte er schneller sein als die Verwendung von find. 

5
Shumoapp

Wenn Sie nur den Inhalt der Dateien markieren und die Dateinamen ignorieren möchten, können Sie dies verwenden

cat $FILES | md5sum

Stellen Sie sicher, dass sich die Dateien in der gleichen Reihenfolge befinden, wenn Sie den Hash berechnen:

cat $(echo $FILES | sort) | md5sum

Sie können jedoch keine Verzeichnisse in Ihrer Liste der Dateien haben.

3
unbeknown

Dafür gibt es ein Python-Skript:

http://code.activestate.com/recipes/576973-getting-the-sha-1-or-md5-hash-of-a-directory/

Wenn Sie die Namen einer Datei ändern, ohne ihre alphabetische Reihenfolge zu ändern, wird sie vom Hash-Skript nicht erkannt. Wenn Sie jedoch die Reihenfolge der Dateien oder den Inhalt einer Datei ändern, erhalten Sie beim Ausführen des Skripts einen anderen Hashwert als zuvor.

2
Kingdon

Ein weiteres Werkzeug, um dies zu erreichen:

http://md5deep.sourceforge.net/

Wie es sich anhört: wie md5sum aber auch rekursiv, plus andere Features.

1
Jack

Ich würde die Ergebnisse für einzelne Dateien über sort (um eine bloße Neuordnung der Dateien zum Ändern des Hashes zu verhindern) in md5sum oder sha1sum umleiten, je nachdem, was Sie wählen.

1
Rafał Dowgird

Ich habe dazu ein Groovy-Skript geschrieben:

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)}"

Sie können die Verwendung anpassen, um das Drucken jeder Datei zu vermeiden, den Message Digest zu ändern, Verzeichnis-Hashing auszuführen usw. Ich habe sie mit den NIST-Testdaten getestet und funktioniert wie erwartet. 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
1
haventchecked

Ein robuster und sauberer Ansatz

  • Zuerst geht es darum, den verfügbaren Speicher nicht zu beanspruchen! Haschieren Sie eine Datei in Abschnitten, anstatt die gesamte Datei einzugeben. 
  • Unterschiedliche Ansätze für unterschiedliche Bedürfnisse/Zwecke (alle unten oder wählen Sie aus, was zutreffend ist):
    • Hash nur den Eintragsnamen aller Einträge in der Verzeichnisstruktur
    • Hash den Dateiinhalt aller Einträge (Verlassen der Meta wie Inode-Nummer, ctime, atime, mtime, size usw., Sie bekommen die Idee)
    • Bei einem symbolischen Link ist der Inhalt der Referenzname. Hash es oder überspringen
    • Folgen Sie dem symbolischen Link oder folgen Sie ihm nicht (aufgelöster Name), während Sie den Inhalt des Eintrags hashen
    • Wenn es sich um ein Verzeichnis handelt, handelt es sich lediglich um Verzeichniseinträge. Während des rekursiven Durchlaufs werden sie schließlich gehasht, aber sollten die Verzeichniseintragsnamen dieser Ebene gehasht werden, um dieses Verzeichnis zu kennzeichnen? Nützlich in Anwendungsfällen, in denen der Hash erforderlich ist, um eine Änderung schnell zu identifizieren, ohne tief in den Inhalt zu gelangen. Ein Beispiel wäre, dass sich der Name einer Datei ändert, der Rest des Inhalts bleibt jedoch derselbe und es sind alles ziemlich große Dateien
    • Behandeln Sie große Dateien gut (beachten Sie auch den Arbeitsspeicher)
    • Behandeln Sie sehr tiefe Verzeichnisbäume (beachten Sie die offenen Dateideskriptoren).
    • Behandeln Sie nicht standardmäßige Dateinamen
    • Wie gehen Sie mit Dateien vor, bei denen es sich um Sockets, Pipes/FIFOs, Blockgeräte, Char-Geräte handelt? Muss sie auch hash machen?
    • Aktualisieren Sie die Zugriffszeit eines Eintrags nicht während des Durchlaufens, da dies für bestimmte Anwendungsfälle ein Nebeneffekt und kontraproduktiv (intuitiv?) Ist.

Das ist, was ich auf meinem Kopf habe, jeder, der einige Zeit damit verbracht hat, hätte praktisch andere Gotchas und Eckfälle erwischt. 

Hier ist ein Werkzeug , sehr leichtes Gedächtnis, das die meisten Fälle anspricht, kann an den Rändern etwas grob sein, war aber recht hilfreich.

Ein Beispiel für die Verwendung und Ausgabe von 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

Ein Ausschnitt aus einer benutzerfreundlichen Ausgabe:

...
... //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
0
six-k

Hier ist eine einfache, kurze Variante in Python 3, die für kleine Dateien (z. B. einen Quellbaum oder etwas anderes, bei dem jede Datei einzeln in RAM leicht passen kann) gut funktioniert, wobei leere Verzeichnisse ignoriert werden, basierend auf den Vorstellungen von andere lösungen:

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()                          

Es funktioniert so:

  1. Alle Dateien im Verzeichnis rekursiv suchen und nach Namen sortieren
  2. Berechne den Hash (Standard: SHA-1) jeder Datei (liest die gesamte Datei in den Speicher)
  3. Erstellen Sie einen Textindex mit den Zeilen "Dateiname = Hash"
  4. Kodieren Sie diesen Index wieder in eine UTF-8-Byte-Zeichenfolge und geben Sie einen Hashwert an

Sie können eine andere Hash-Funktion als zweiten Parameter übergeben, wenn SHA-1 nicht Ihre Tasse Tee ist.

0
Thomas Perl

Versuchen Sie es in zwei Schritten zu machen:

  1. erstellen Sie eine Datei mit Hashwerten für alle Dateien in einem Ordner
  2. hash diese Datei

So wie:

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

Oder machen Sie alles auf einmal:

# cat `find /folder/of/stuff -type f | sort` | sha1sum
0
Joao da Silva

Sie können sha1sum zum Generieren der Liste der Hashwerte und dann sha1sum dieser Liste erneut verwenden. Dies hängt davon ab, was genau Sie erreichen möchten.

0
Ronny Vindenes

Ich musste in einem ganzen Verzeichnis nach Dateiänderungen suchen. 

Aber mit ausschließlichen Zeitstempeln, Verzeichnisseigentum.

Ziel ist es, überall eine identische Summe zu erhalten, wenn die Dateien identisch sind.

Einbindung in andere Rechner, unabhängig von den Dateien oder einer Änderung.

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

Es generiert eine Liste von Hashs nach Datei und verknüpft diese Hashes dann zu einem Hash.

Dies ist viel schneller als die Teer-Methode.

Für eine stärkere Privacy in unseren Hashes können wir sha512sum für dasselbe Rezept verwenden.

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

Die Hashes sind auch überall identisch mit sha512sum , aber es gibt keine bekannte Möglichkeit, sie umzukehren.

0
Cryptopat

Wenn dies ein Git-Repo ist und Sie Dateien in .gitignore ignorieren möchten, können Sie dies verwenden:

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

Das funktioniert gut für mich.

0
ndbroadbent