md5sumプログラムは、ディレクトリのチェックサムを提供しません。サブディレクトリ内のファイルを含む、ディレクトリのコンテンツ全体に対して単一のMD5チェックサムを取得したい。つまり、すべてのファイルから作成された1つの結合チェックサムです。これを行う方法はありますか?
md5sumプログラムは、ディレクトリのチェックサムを提供しません。サブディレクトリ内のファイルを含む、ディレクトリのコンテンツ全体に対して単一のMD5チェックサムを取得したい。つまり、すべてのファイルから作成された1つの結合チェックサムです。これを行う方法はありますか?
回答:
正しい方法は、あなたが尋ねている正確な理由に依存します:
ツリーのファイル内容のハッシュが必要な場合、これでうまくいきます:
$ find -s somedir -type f -exec md5sum {} \; | md5sum
これは、最初にすべてのファイルの内容を予測可能な順序で個別に要約し、次にファイル名とMD5ハッシュのリストをハッシュしてそれ自体をハッシュし、ツリー内のファイルの1つの内容が変更されたときにのみ変更される単一の値を与えます。
残念ながら、find -s
macOS、FreeBSD、NetBSD、OpenBSDで使用されるBSD find(1)でのみ動作します。GNUまたはSUS find(1)を備えたシステムで同等のものを取得するには、少しbitいものが必要です。
$ find somedir -type f -exec md5sum {} \; | sort -k 2 | md5sum
のfind -s
呼び出しに置き換えましたsort
。この-k 2
ビットは、MD5ハッシュをスキップするように指示しているため、フィールド2から行末までのファイル名のみを、sort
推測によってソートします。
このバージョンのコマンドには弱点があります。ファイル名に改行が含まれていると混乱する可能性がありsort
ます。これは、呼び出しに対して複数行のように見えるためです。find -s
ツリーのトラバースとソートは同じプログラム内で行われるため、バリアントにはこの問題はありませんfind
。
どちらの場合でも、誤検知を避けるためにソートが必要です。最も一般的なUnix / Linuxファイルシステムは、安定した予測可能な順序でディレクトリリストを維持しません。ls
ディレクトリの内容をサイレントにソートするを使用することでこれを実現できないかもしれません。find
なし、-s
またはsort
呼び出しは、基礎となるファイルシステムが返す順序でファイルを出力します。入力としてファイルに与えられた順序が変わると、このコマンドは変更されたハッシュ値を与えます。
md5sum
コマンドmd5
または他のハッシュ関数を変更する必要がある場合があります。別のハッシュ関数を選択し、システムにコマンドの2番目の形式が必要な場合は、sort
それに応じてコマンドを調整する必要があります。もう1つの落とし穴は、一部のデータ集計プログラムがファイル名をまったく書き出さないことsum
です。これは、古いUnix プログラムが主な例です。
この方法はやや非効率的で、md5sum
N + 1回呼び出します。Nはツリー内のファイルの数ですが、ファイルとディレクトリのメタデータのハッシュを避けるために必要なコストです。
ファイルの内容だけでなく、ツリー内の何かが変更されたことを検出できるようにする必要がある場合はtar
、ディレクトリの内容を圧縮してから、次の宛先に送信してmd5sum
ください。
$ tar -cf - somedir | md5sum
のでtar
等もファイルのアクセス権、所有権を、見て、これはまたそれらのものへの変更を検出します、ただ内容をファイルに変更はありません。
この方法は、ツリーを1回だけ通過し、ハッシュプログラムを1回だけ実行するため、かなり高速です。
find
上記のベースのメソッドと同様に、基にtar
なるファイルシステムが返す順にファイル名を処理します。アプリケーションで、これが発生しないことを確認できます。少なくとも3つの異なる使用パターンが考えられます。(私たちは不特定の動作領域に入るため、それらをリストするつもりはありません。各ファイルシステムは、OSのバージョンによっても、ここでは異なる場合があります。)
誤検知が発生しているfind | cpio
場合は、Gillesの回答のオプションを選択することをお勧めします。
find somedir -type f -exec sh -c "openssl dgst -sha1 -binary {} | xxd -p" \; | sort | openssl dgst -sha1
すべてのファイル名を無視する(改行で動作するはずです)
チェックサムは、文字列としてのファイルの明確で明確な表現である必要があります。確定的とは、同じ場所に同じファイルを配置すると、同じ結果が得られることを意味します。明確なとは、2つの異なるファイルセットが異なる表現を持つことを意味します。
ファイルを含むアーカイブを作成することは良い出発点です。これは明確な表現です(明らかに、アーカイブを抽出することでファイルを回復できるため)。日付や所有権などのファイルメタデータが含まれる場合があります。ただし、これはまだ適切ではありません。アーカイブの表現は、ファイルの保存順序に依存するため、また圧縮に該当する場合、アーカイブはあいまいです。
解決策は、アーカイブする前にファイル名をソートすることです。ファイル名に改行が含まれていない場合は、実行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
2つの部分を分離します(これがないと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
複数のマシンおよびOSで実行している場合、並べ替え順序の設定は不可欠です。
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
2つのディレクトリの内容を比較するときにファイルの絶対パスが干渉しないように「相対パスを使用」を-j0
意味します。個々のmd5sumが異なる順序で返されます)。
2つのディレクトリ間の違いを見つけることだけが目的の場合は、diffの使用を検討してください。
これを試して:
diff -qr dir1 dir2
すべてのファイルを再帰的にハッシュし、結果のテキストをハッシュできます。
> md5deep -r -l . | sort | md5sum
d43417958e47758c6405b5098f151074 *-
md5deepが必要です。
md5deep
使用する代わりにhashdeep
。
## Invoked from: /home/myuser/dev/
、現在のパスとを含むヘッダーも出力します## $ hashdeep -s -r -l ~/folder/
。これはソートするようになったため、現在のフォルダーまたはコマンドラインを変更すると、最終的なハッシュが異なります。
内容が異なるディレクトリにあるため、ファイル名のみをチェックするバージョンが必要でした。
このバージョン(ウォーレンヤングの答え)は大いに役立ちましたが、私のバージョンでmd5sum
はファイル名(コマンドを実行したパスに関連する)が出力され、フォルダー名が異なっていたため、個々のファイルのチェックサムが一致しても最終チェックサムは一致しませんでした't。
これを修正するには、私の場合、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
作品の高速かつ簡単に次いでこの溶液のbashスクリプトを。
ドキュメントを参照してください:https : //pypi.python.org/pypi/checksumdir/1.0.5
nix-hash
ニックスパッケージマネージャ
コマンドnix-hashは、各パスのコンテンツの暗号化ハッシュを計算し、標準出力に出力します。デフォルトでは、MD5ハッシュを計算しますが、他のハッシュアルゴリズムも利用できます。ハッシュは16進数で出力されます。
ハッシュは、各パスのシリアル化(パスをルートとするファイルシステムツリーのダンプ)で計算されます。これにより、通常のファイルと同様にディレクトリとシンボリックリンクをハッシュできます。ダンプは、nix-store --dumpによって生成されるNAR形式です。したがって、nix-hashパスは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.
この優れた答えのフォローアップとして、大きなディレクトリのチェックサムの計算を高速化したい場合は、GNU Parallelを試してください:
find -s somedir -type f | parallel -k -n 100 md5 {} | md5
(これはMacを使用しておりmd5
、必要に応じて交換してください。)
-k
フラグは重要です。このフラグはparallel
順序を維持するよう指示します。そうしないと、ファイルがすべて同じであっても、全体の合計が実行を変更する可能性があります。100個の引数での-n 100
各インスタンスを実行するようにmd5
指示されていますが、これは最適な実行時間に調整できるパラメーターです。の-X
フラグも参照してくださいparallel
(ただし、個人的なケースではエラーが発生しました)。
十分にテストされ、重複の検索、データとメタデータの両方の比較、追加と変更および削除の表示を含む多くの操作をサポートするスクリプトは、指紋が好きかもしれません。
現在、指紋はディレクトリの単一のチェックサムを生成するのではなく、そのディレクトリ内のすべてのファイルのチェックサムを含むトランスクリプトファイルを生成します。
fingerprint analyze
これによりindex.fingerprint
、チェックサム、ファイル名、ファイルサイズを含む現在のディレクトリに生成されます。デフォルトでは、MD5
との両方を使用しますSHA1.256
。
将来的には、指紋にMerkle Treeのサポートを追加して、単一のトップレベルチェックサムを提供したいと考えています。現時点では、検証を行うためにそのファイルを保持する必要があります。
私は新しい実行可能ファイルや不格好なソリューションが欲しくなかったので、ここに私の見解を示します。
#!/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
この回答は、先ほどウォーレンヤングとジルの優れた回答で提案されていたように、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で利用可能なさまざまなアーカイブ形式についての素晴らしいレビューも提供します。)
私たちの場合、ディレクトリの内容をtarし、結果をハッシュし、特定の措置を講じることなく、GNUからPOSIX形式への変更は次の結果をもたらします。
同じディレクトリ内容にもかかわらず、結果のチェックサムは異なります。
ディレクトリーの内容が同一であるにもかかわらず、デフォルトのpaxヘッダーが使用されている場合、結果のチェックサムは実行ごとに異なります。
後者は、POSIX(pax)形式にはデフォルトの%d/PaxHeaders.%p/%f
GNU Tarの形式文字列によって決定される拡張paxヘッダーが含まれているという事実に由来しています。この文字列内で、指定子%p
は生成するTarプロセスのプロセスIDに置き換えられます。これはもちろん実行ごとに異なります。参照してください。このセクションのGNUタールマニュアル、特にこの1詳細については、を。
ちょうど今、2019-03-28から、この問題を解消するコミットがアップストリームで受け入れられています。
そのため、特定のユースケースでGNU Tarを引き続き使用できるようにするには、次の代替オプションをお勧めします。
Tarオプション--format=gnu
を使用して、「古い」形式でアーカイブを生成するようTarに明示的に指示します。これは、「古い」チェックサムを検証するために必須です。
新しいPOSIX形式を使用しますが、適切なpaxヘッダーを明示的に指定します(例:)--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
設定をサブシェル内でローカルに保ちます。
さらに、ソケットファイルがディレクトリの内容の一部である場合に発生するTarからの警告を回避するために式! -type s
with を使用しfind
ます。GNUTarはソケットをアーカイブしません。スキップされたソケットに関する通知を希望する場合は、その式を残します。
私は--numeric-owner
Tar を使用して、ファイル所有者のすべてが知られていないシステムでもチェックサムを後で検証できるようにします。
--atime-preserve
Tar のオプションは<paths>
、読み取り専用でマウントされたデバイス上にある場合は省略したほうがよいでしょう。そうしないと、アクセスタイムスタンプTarが復元できなかった個々のファイルごとに警告が表示されます。書き込み可能の<paths>
場合、このオプションを使用して、ハッシュされたディレクトリのアクセスタイムスタンプを保持します。
Gillesの提案で--no-recursion
すでに使用されているTarオプションは、Tar が単独でディレクトリに再帰的に下降することを防ぎ、代わりにソートされた出力から供給されるものをファイルごとに操作することを防ぎます。find
最後に、私が使用しているのは正しくありません。md5sum
実際に使用していますsha256sum
。
find .
代わりに使用するのが最善だと思いますfind somedir
。このように、異なるパス指定を指定して検索する場合、ファイル名は同じです。これには注意が必要です: