Linux:特定のフォルダーとコンテンツの単一のハッシュを計算しますか?


95

きっとこれを簡単に行う方法があるはずです!

私は、Linuxのコマンドラインなどを試したアプリsha1summd5sum、彼らは唯一の個々のファイルや出力ハッシュ値のリスト、各ファイルに1つのハッシュを計算することができるように思われます。

(ファイル名だけでなく)フォルダーのコンテンツ全体に対して単一のハッシュを生成する必要があります。

次のようなことをしたい

sha1sum /folder/of/stuff > singlehashvalue

編集:明確にするために、私のファイルはディレクトリツリーの複数のレベルにあり、すべてが同じルートフォルダーにあるわけではありません。


1
「内容全体」とは、ルートハッシュに到達する際の、ディレクトリ内のすべてのファイルの論理データ、またはメタと一緒のデータを意味しますか?あなたのユースケースの選択基準はかなり広いので、私は答えでいくつかの実用的なものに対処しようとしました。
6 k

回答:


123

1つの可能な方法は次のとおりです。

sha1sum path / to / folder / * | sha1sum

ディレクトリツリー全体が存在する場合は、findとxargsを使用した方がよいでしょう。1つの可能なコマンドは

path / to / folder -type f -print0 | ソート-z | xargs -0 sha1sum | sha1sum

そして最後に、権限と空のディレクトリも考慮する必要がある場合:

(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

への引数statにより、ファイル名が出力され、その後に8進数のアクセス権が出力されます。2つの検索は順番に実行され、ディスクIOの量が2倍になります。最初の検索はすべてのファイル名の検索と内容のチェックサム計算、2番目の検索はすべてのファイルとディレクトリ名の検索、名前とモードの出力です。次に、「ファイル名とチェックサム」のリストの後に「名前とディレクトリ、アクセス許可」が続くため、チェックサムが小さくなります。


2
LC_ALL = POSIXを設定することを忘れないでください。そのため、さまざまなツールがロケールに依存しない出力を作成します。
デビッドシュミット

2
猫を見つけた| sha1sumはsha1sumよりもかなり高速です| sha1sum。YMMV、あなたのシステムでこれらのそれぞれを試してください:time path / to / folder -type f -print0 | ソート-z | xargs -0 sha1sum | sha1sum; 時間パス/宛先/フォルダを見つける-type f -print0 | ソート-z | xargs -0猫| sha1sum
ブルーノブロノスキー

5
@RichardBronosky-AとBの2つのファイルがあるとします。Aには「foo」が含まれ、Bには「bar was here」が含まれます。あなたの方法では、CとDの2つのファイルからそれを分離することはできません。Cは「foobar」を含み、Dは「ここにありました」を含みます。各ファイルを個別にハッシュしてから、すべての「ファイル名ハッシュ」ペアをハッシュすることで、違いを確認できます。
Vatine

2
ディレクトリ・パスに関係なく、この作業を行うパスは、最終的なハッシュに含まれているので(つまり、2つの異なるフォルダのハッシュを比較したいとき)、あなたは、適切なディレクトリへの相対パスと変更を使用する必要がありますするには:find ./folder -type f -print0 | sort -z | xargs -0 sha1sum | sha1sum
robbles

3
@robblesそれは正しいです、そして私が少し頭文字/をつけなかった理由path/to/folder
Vatine 2013

25
  • aideなどのファイルシステム侵入検知ツールを使用します。

  • ディレクトリのtarボールをハッシュします。

    tar cvf - /path/to/folder | sha1sum

  • vatineのonelinerのように、自分でコーディングします。

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


3
tarソリューションの場合は+1。これが最速ですが、冗長性を落とすと速度が低下するだけです。
Bruno Bronosky 2013

6
tarソリューションは、ファイルを比較するときに、ファイルが同じ順序であると想定していることに注意してください。それらが存在するかどうかは、比較を行うときにファイルが存在するファイルシステムに依存します。
nos

5
ファイルの内容は入力の一部にすぎないため、gitハッシュはこの目的には適していません。ブランチの最初のコミットでも、ハッシュはコミットの時間と同様に、コミットメッセージとコミットメタデータの影響を受けます。同じディレクトリ構造を複数回コミットすると、毎回異なるハッシュを取得するため、結果のハッシュは、ハッシュを送信するだけで2つのディレクトリがお互いの正確なコピーであるかどうかを判断するのに適していません。
ゾルタン

1
@Zoltanコミットハッシュではなくツリーハッシュを使用する場合、gitハッシュは完全に問題ありません。
ホッブズ

@hobbs答えはもともと「コミットハッシュ」と述べていましたが、これは確かにこの目的には適していません。ツリーハッシュははるかに優れた候補のように聞こえますが、まだ隠されたトラップがある可能性があります。私の頭に浮かぶのは、一部のファイルに実行可能ビットを設定すると、ツリーハッシュが変更されることです。git config --local core.fileMode falseこれを回避するには、コミットする前に発行する必要があります。このような警告があるかどうかはわかりません。
ゾルタン

14

できるよ tar -c /path/to/folder | sha1sum


16
別のマシンでそのチェックサムを複製する場合は、形式に曖昧さの余地があり、多くのバージョンに存在するように見えるため、tarは適切な選択ではない可能性があります。そのため、別のマシンのtarは同じファイルから異なる出力を生成する可能性があります。
スロードッグ

2
slowdogの有効な懸念にもかかわらず、ファイルの内容、権限などを気にしていて、変更時間を気にしない場合は、次の--mtimeようなオプションを追加できますtar -c /path/to/folder --mtime="1970-01-01" | sha1sum
Binary Phile 2015

@ S.Lottディレクトリのサイズが大きい場合、つまりディレクトリのサイズが非常に大きい場合、圧縮してmd5を取得するのに時間がかかります
Kasun Siyambalapitiya

13

フォルダー内の何かが変更されたかどうかを確認するだけの場合は、次の方法をお勧めします。

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

これは、フォルダー、サブフォルダー、それらのファイル、それらのタイムスタンプ、サイズ、およびアクセス許可を含むls出力のハッシュを提供します。何かが変更されたかどうかを判断するために必要なほとんどすべてのこと。

このコマンドは各ファイルのハッシュを生成しないことに注意してください。そのため、findを使用するよりも高速である必要があります。


1
ソリューションの単純さを考えると、これが賛成票をこれ以上持たない理由はわかりません。これがうまくいかない理由を誰かが説明できますか?
Dave C

1
生成されたハッシュはファイルの所有者、日付形式の設定などに基づくため、これは理想的ではないと思います
Ryota

1
lsコマンドをカスタマイズして、必要なものを出力できます。-lを-gGに置き換えると、グループと所有者を省略できます。また、-time-styleオプションを使用して日付形式を変更できます。基本的にls manページをチェックして、ニーズに合ったものを確認してください。
Shumoapp 2017年

@DaveCそれはほとんど役に立たないからです。ファイル名を比較したい場合は、直接比較してください。彼らはそれほど大きくありません。
Navin、

7
@Navin質問から、ファイルの内容をハッシュする必要があるのか​​、ツリーの変更を検出する必要があるのか​​は明確ではありません。それぞれのケースには用途があります。たとえば、カーネルツリーに45Kのファイル名を格納することは、単一のハッシュよりも実用的ではありません。ls -lAgGR --block-size = 1 --time-style = +%s | sha1sumは私にとって素晴らしい働きをします
yashma 2018

5

堅牢でクリーンなアプローチ

  • まず最初に、利用可能なメモリを独占しないください!ファイル全体をフィードするのではなく、ファイルをチャンクでハッシュします。
  • さまざまなニーズ/目的のためのさまざまなアプローチ(以下のすべてまたはこれまでに当てはまるものを選択してください):
    • ディレクトリツリー内のすべてのエントリのエントリ名のみをハッシュする
    • すべてのエントリのファイルの内容をハッシュします(メタのような、iノード番号、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.
  -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

人間に優しい出力のスニペット:

...
... //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

1
フォルダーのロバストでクリーンなsha256を取得するための簡単な例を挙げてください。Windowsフォルダーに3つのサブディレクトリがあり、それぞれにいくつかのファイルがある場合はどうでしょうか。
フェリット

3

ファイルの内容をハッシュしてファイル名を無視したい場合は、

cat $FILES | md5sum

ハッシュを計算するときは、ファイルが同じ順序であることを確認してください。

cat $(echo $FILES | sort) | md5sum

ただし、ファイルのリストにディレクトリを含めることはできません。


2
1つのファイルの末尾をアルファベット順でそれに続くファイルの先頭に移動しても、ハッシュには影響しませんが、影響はあります。ファイル区切り記号またはファイル長をハッシュに含める必要があります。
Jason Stangroome

3

これを実現する別のツール:

http://md5deep.sourceforge.net/

音として:md5sumと同様ですが、再帰的であり、その他の機能もあります。


1
このリンクで質問に答えることができますが、回答の重要な部分をここに含め、参照用のリンクを提供することをお勧めします。リンクされたページが変更されると、リンクのみの回答が無効になる可能性があります。
Mamoun Benghezal

3

これがgitリポジトリであり、のファイルをすべて無視したい場合は.gitignore、次のように使用できます。

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

これは私にとってはうまくいきます。


どうもありがとう!:)
visortelle

多くのアプリケーションでは、このアプローチが優れています。ソースコードファイルだけをハッシュすると、はるかに少ない時間で十分にユニークなハッシュが得られます。
John McGehee


1

2つのステップで作成してみてください。

  1. フォルダ内のすべてのファイルのハッシュを含むファイルを作成する
  2. このファイルをハッシュ

そのようです:

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

または、一度にすべて実行します。

# cat `find /folder/of/stuff -type f | sort` | sha1sum

for F in 'find ...' ...名前にスペースが含まれている場合は機能しません(今日は常にそうです)。
mivk

1

個々のファイルの結果をパイプ処理してsort(ハッシュを変更するためにファイルを並べ替えるのを防ぐため)、md5sumまたはのsha1sumどちらかを選択します。


1

これを行うためのGroovyスクリプトを作成しました。

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

使用法をカスタマイズして、各ファイルの印刷を回避したり、メッセージダイジェストを変更したり、ディレクトリハッシュを削除したりできます。私はNISTテストデータに対してテストしましたが、期待どおりに動作します。 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

ファイルの変更については、ディレクトリ全体をチェックインする必要がありました。

ただし、タイムスタンプ、ディレクトリの所有権は除外されます。

ファイルが同じであれば、どこでも同じ合計を得ることが目標です。

他のマシンにホストされているものを含め、ファイル以外のもの、またはそれらへの変更。

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

ファイルごとにハッシュのリストを生成し、それらのハッシュを1つに連結します。

これはtarメソッドよりもはるかに高速です。

以下のために強力なプライバシーたちのハッシュで、我々は使用することができますsha512sumを同じレシピで。

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

ハッシュは、sha512sumを使用してもどこでも同じですが、逆にする既知の方法はありません。


これは、ディレクトリをハッシュするための受け入れられた答えよりもはるかに単純なようです。受け入れられた答えが信頼できるとは思いませんでした。1つの問題...ハッシュが別の順序で出てくる可能性はありますか?sha256sum /tmp/thd-agent/* | sort信頼できる順序付けのために私が試しているのは、それをハッシュ化することです。
thinktt

こんにちは、ハッシュはデフォルトでアルファベット順になっているようです。信頼できる注文とはどういう意味ですか?すべてを自分で整理する必要があります。たとえば、連想配列、エントリ+ハッシュを使用します。次に、この配列をエントリでソートすると、計算されたハッシュのリストがソート順に表示されます。それ以外の場合はjsonオブジェクトを使用して、オブジェクト全体を直接ハッシュできると思います。
NVRM

私が理解しているとしたら、それはファイルをアルファベット順にハッシュするということです。そうですね。上記の受け入れられた回答の一部が断続的に異なる注文を時々与えていたので、私はそれが再び起こらないことを確認しようとしています。最後に並べ替えを続けることにします。動作しているようです。この方法と受け入れられた回答の唯一の問題は、ネストされたフォルダを処理しないことです。私の場合、フォルダがないのでこれはうまくいきます。
thinktt

どうls -r | sha256sumですか?
NVRM

@NVRMが試行し、ファイルの内容ではなくファイル名の変更を確認しただけ
Gi0rgi0s

0

sha1sumハッシュ値のリストを生成してから、sha1sumそのリストを再度生成することもできます。それは、何を達成したいかによります。


0

他のソリューションからのアイデアに基づいて、空のディレクトリを無視して、小さなファイル(たとえば、ソースツリーなど、すべてのファイルが個別にRAMに簡単に収まるようなもの)でうまく機能するPython 3のシンプルな短いバリアントを次に示します。

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

それはこのように動作します:

  1. ディレクトリ内のすべてのファイルを再帰的に検索し、名前順に並べ替えます
  2. すべてのファイルのハッシュ(デフォルト:SHA-1)を計算します(ファイル全体をメモリに読み込みます)
  3. "filename = hash"行でテキストインデックスを作成します
  4. そのインデックスをエンコードしてUTF-8バイト文字列に戻し、それをハッシュします

SHA-1がお茶ではない場合は、別のハッシュ関数を2番目のパラメーターとして渡すことができます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.