Md5sumプログラムは、ディレクトリのチェックサムを提供しません。サブディレクトリ内のファイルを含む、ディレクトリのコンテンツ全体の単一のMD5チェックサムを取得したいと考えています。つまり、すべてのファイルから作成された1つの結合されたチェックサム。これを行う方法はありますか?
正しい方法は、なぜあなたが尋ねているのかに正確に依存します:
ツリーのファイル内容のハッシュだけが必要な場合は、これでうまくいきます。
$ find -s somedir -type f -exec md5sum {} \; | md5sum
これは最初にすべてのファイルの内容を予測可能な順序で個別に要約し、次にファイル名のリストとそれ自体がハッシュされるMD5ハッシュを渡し、ツリー内のいずれかのファイルのコンテンツが変更された場合にのみ変更される単一の値を提供します。
残念ながら、find -s
は、macOS、FreeBSD、NetBSDおよびOpenBSDで使用されているBSD find(1)でのみ機能します。 GNUまたはSUS find(1))を使用してシステムで同等のものを取得するには、少し醜いものが必要です。
$ find somedir -type f -exec md5sum {} \; | sort -k 2 | md5sum
sort
への呼び出しを追加することにより、BSD find -s
の動作を模倣しました。 -k 2
ビットは、MD5ハッシュをスキップするように指示するため、フィールド2にあるファイル名のみをsort
の計算によってソートします。
このバージョンのコマンドには弱点があります。つまり、改行が含まれているファイル名がある場合、sort
呼び出しに対して複数行のように見えるため、混乱する可能性があります。ツリーの走査とソートは同じプログラムfind
内で行われるため、find -s
バリアントにはその問題はありません。
どちらの場合でも、誤検知を回避するためにソートが必要です。最も一般的なUnix/Linuxファイルシステムは、ディレクトリリストを安定した予測可能な順序で維持しません。 ls
などを使用しても、ディレクトリの内容が自動的にソートされるため、これに気付かない場合があります。何らかの方法で出力をソートせずにfind
を呼び出すと、出力の行の順序が、基になるファイルシステムが返す順序と一致します。これにより、ファイルの順序が指定されている場合、このコマンドはハッシュ値を変更しますデータが同一のままであったとしても、入力が変化するとそれに応じて。
上記のGNU sort
コマンドの-k 2
ビットが必要かどうかを確認することもできます。ファイルのデータのハッシュがファイルの内容が変更されていない限り、このオプションを削除しても誤検知は発生せず、GNUとBSD sort
の両方で同じコマンドを使用できます。ただし、小さなチャンスがあることを認識してください(1:2128 MD5の場合)ファイル名の正確な順序は、ハッシュの衝突があった場合に-k 2
なしで実行できる部分的な順序と一致しません。ただし、アプリケーションでミスマッチのこのような小さな可能性が問題になる場合は、このアプローチ全体はおそらく問題外です。
md5sum
コマンドをmd5
またはその他のハッシュ関数に変更する必要がある場合があります。別のハッシュ関数を選択し、システムでコマンドの2番目の形式が必要な場合は、それに応じてsort
コマンドを調整する必要があります。別のトラップは、一部のデータ集計プログラムがファイル名をまったく書き出さないことです。主な例は、古いUnix sum
プログラムです。
この方法は多少効率が悪く、md5sum
をN + 1回呼び出します。Nはツリー内のファイル数ですが、ファイルとディレクトリのメタデータのハッシュを回避するために必要なコストです。
ファイルのコンテンツだけでなく、ツリー内のanythingが変更されたことを検出できるようにする必要がある場合は、tar
にディレクトリをパックするよう依頼してください。内容を準備してから、md5sum
に送信します。
$ tar -cf - somedir | md5sum
tar
にはファイルのアクセス許可や所有権なども表示されるため、ファイルの内容に対する変更だけでなく、それらに対する変更も検出されます。
このメソッドは、ツリーを1回だけ通過し、ハッシュプログラムを1回だけ実行するため、かなり高速です。
上記のfind
ベースのメソッドと同様に、tar
は、基礎となるファイルシステムが返す順序でファイル名を処理します。あなたのアプリケーションでは、これが起こらないようにすることができるでしょう。そうなる可能性が高い、少なくとも3つの異なる使用パターンを考えることができます。 (私たちは不特定の行動領域に入るので、それらをリストアップするつもりはありません。ここでは、OSのバージョンによっても、各ファイルシステムは異なる場合があります。)
誤検知が発生する場合は、 ギレスの回答 のfind | cpio
オプションを使用することをお勧めします。
チェックサムは、ファイルの文字列としての確定的で明確な表現である必要があります。確定的とは、同じファイルを同じ場所に置いても、同じ結果が得られることを意味します。明確とは、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
出力のような空のディレクトリになり、通常のファイルにも渡すことができます)。 length-extension attack を回避するために、ファイルサイズのリストも含まれています。
ちなみに、MD5は非推奨です。利用できる場合は、SHA-2、または少なくともSHA-1の使用を検討してください。
GNUツールを使用してファイル名をnullバイトで分離する上記のコードのバリアントです。これにより、ファイル名に改行を含めることができます。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()
md5deep をご覧ください。興味があるかもしれないmd5deepの機能のいくつか:
再帰的な操作-md5deepはディレクトリツリー全体を再帰的に検査できます。つまり、ディレクトリ内のすべてのファイルとすべてのサブディレクトリ内のすべてのファイルのMD5を計算します。
比較モード-md5deepは既知のハッシュのリストを受け入れ、それらを一連の入力ファイルと比較できます。プログラムは、既知のハッシュのリストに一致する入力ファイルまたは一致しない入力ファイルを表示できます。
...
2つのディレクトリ間の違いを見つけるだけの場合は、diffの使用を検討してください。
これを試して:
diff -qr dir1 dir2
すべてのファイルを再帰的にハッシュしてから、結果のテキストをハッシュできます。
> md5deep -r -l . | sort | md5sum
d43417958e47758c6405b5098f151074 *-
md5deep が必要です。
ソリューション:
$ pip install checksumdir
$ checksumdir -a md5 assets/js
981ac0bc890de594a9f2f40e00f13872
$ checksumdir -a sha1 assets/js
88cd20f115e31a1e1ae381f7291d0c8cd3b92fad
fastとeasierソリューションが機能し、bashスクリプトが機能します。
ドキュメントを参照してください: https://pypi.python.org/pypi/checksumdir/1.0.5
この優れた答え のフォローアップとして、大きなディレクトリのチェックサムの計算を高速化したい場合は、 GNU Parallel を試してください。
find -s somedir -type f | parallel -k -n 100 md5 {} | md5
(これはmd5
を備えたMacを使用しています。必要に応じて交換してください。)
-k
フラグは重要です。これはparallel
に順序を維持するように指示します。そうしないと、ファイルがすべて同じであっても、全体の合計が実行に変わる可能性があります。 -n 100
は、md5
の各インスタンスを100個の引数で実行することを示しています。これは、実行時間を最適化するために調整できるパラメーターです。 parallel
の-X
フラグも参照してください(私の個人的なケースではエラーが発生しました)。
私はこのスニペットを 中程度のボリューム に使用します:
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 -
内容が異なるディレクトリにあるため、ファイル名のみをチェックするバージョンが必要でした。
このバージョン(ウォーレンヤングの回答) は大いに役立ちましたが、私のバージョンのmd5sum
はファイル名(コマンドを実行したパスからの相対パス)を出力し、フォルダー名が異なっていたため、個々のファイルのチェックサムは一致しましたが、最終的なチェックサムは一致しませんでした。
これを修正するには、私の場合、find
出力の各行からファイル名を削除する必要がありました(cut
を使用してスペースで区切られた最初の単語のみを選択してください):
find -s somedir -type f -exec md5sum {} \; | cut -d" " -f1 | md5sum
nix-hash
from Nixパッケージマネージャー
コマンドnix-hashは、各パスの内容の暗号化ハッシュを計算し、標準出力に出力します。デフォルトでは、MD5ハッシュを計算しますが、他のハッシュアルゴリズムも使用できます。ハッシュは16進数で出力されます。
ハッシュは、各パスのシリアル化で計算されます。パスをルートとするファイルシステムツリーのダンプです。これにより、ディレクトリとシンボリックリンクだけでなく、通常のファイルもハッシュ化できます。ダンプは、nix-store --dumpによって生成されるNAR形式です。したがって、nix-hashパスはnix-store --dump pathと同じ暗号ハッシュを生成します。 md5sum。
優れたツリーチェックサムは、GitのツリーIDです。
残念ながら、それを実行できるスタンドアロンツールはありません(少なくとも私はそれを知りません)。Gitが手元にある場合は、新しいリポジトリを設定して、チェックするファイルをインデックスに追加するふりをすることができます。
これにより、(再現可能な)ツリーハッシュを作成できます。これには、コンテンツ、ファイル名、一部の縮小ファイルモード(実行可能ファイル)のみが含まれます。
新しい実行可能ファイルも不格好なソリューションも欲しくないので、ここに私の見解を示します。
#!/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
十分にテストされ、重複の検索、データとメタデータの両方での比較、追加と変更および削除の表示など、さまざまな操作をサポートするスクリプトは、 Fingerprint のようになります。
現在、指紋はディレクトリの単一のチェックサムを生成しませんが、そのディレクトリ内のすべてのファイルのチェックサムを含むトランスクリプトファイルを生成します。
fingerprint analyze
これにより、チェックサム、ファイル名、ファイルサイズを含むindex.fingerprint
が現在のディレクトリに生成されます。デフォルトでは、MD5
とSHA1.256
の両方が使用されます。
将来的には、単一のトップレベルのチェックサムを提供するフィンガープリントにマークルツリーのサポートを追加したいと思っています。現在、検証を行うためにそのファイルを保持する必要があります。
一発ギャグ:
find directory -exec md5sum {} \; 2>&1 | sort -k 2 | md5sum
これにより、すべてのファイルとディレクトリがリストされ、それぞれのmd5sum
が取得されます。次に、すべてについてmd5sum
を取得します。
md5sum
がディレクトリの合計を実行できないことがここで解決されましたが、これはmd5sum: dir/sub_dir: Is a directory
にわかります。このメッセージを標準出力に移動するだけです。
各ディレクトリのすべてのファイルに対して個別に行う。
# Calculating
find dir1 | xargs md5sum > dir1.md5
find dir2 | xargs md5sum > dir2.md5
# Comparing (and showing the difference)
paste <(sort -k2 dir1.md5) <(sort -k2 dir2.md5) | awk '$1 != $3'
この回答は、 Warren Young および Gillesの優れた回答で(とりわけ)提案されたように、Tar出力を使用してディレクトリのコンテンツをハッシュする方法に対する補足的な更新を目的としています。 少し前。
それ以来、少なくともopenSUSE(そのリリース12.2以降)はデフォルトのGNU Tar形式を "GNU tar 1.13.x format"(やや)上位「POSIX 1003.1-2001(pax)形式」に変換されます。また、上流(GNU Tarの開発者の間)が同じ移行を実行することについて議論しています。たとえば、 このページの最後の段落GNUを参照してください_タールマニュアル :
GNU tarのデフォルトのフォーマットは、コンパイル時に定義されます。
tar --help
を実行して、出力の最後の行を調べることで確認できます。通常、GNU tarは、gnu
形式でアーカイブを作成するように構成されていますが、将来のバージョンではposix
に切り替わります。
(このページでは、GNU Tarで利用できるさまざまなアーカイブ形式についての素晴らしいレビューも提供します。)
私たちのケースでは、ディレクトリの内容をtarして結果をハッシュし、特定の対策を講じずに、GNUからPOSIX形式に変更すると、次のような影響があります。
同じディレクトリの内容にもかかわらず、結果のチェックサムは異なります。
同一のディレクトリ内容にもかかわらず、デフォルトのpaxヘッダーが使用されている場合、結果のチェックサムは実行ごとに異なります。
後者は、POSIX(pax)形式には、GNU Tarのデフォルトの%d/PaxHeaders.%p/%f
である形式文字列によって決定される拡張paxヘッダーが含まれているという事実から来ています。この文字列内で、指定子%p
は、生成されるTarプロセスのプロセスIDで置き換えられます。もちろん、これは実行ごとに異なります。詳細については、 このセクション の GNU Tarマニュアル を参照してください。特に これ を参照してください。
ちょうど今、2019年3月28日以降、この問題を解決する commit 受け入れられたアップストリームがあります。
したがって、特定のユースケースで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ロケールの使用、ファイル名のnullバイト分離、および検索とソートを使用してアーカイブ内のファイルのファイルシステムに依存しない順序を取得する目的は、他の回答ですでに十分に説明されています。
括弧で囲んでいると、サブシェルのLC_ALL
設定がローカルに保持されます。
さらに、ソケットファイルがディレクトリの内容の一部である場合に発生するTarからの警告を回避するために、! -type s
という式をfind
とともに使用します。GNU Tarはソケットをアーカイブしません。スキップされたソケットについて通知を受け取りたい場合は、その式を省略します。
私はTarで--numeric-owner
を使用して、すべてのファイル所有者がわかっていないシステムでも、後でチェックサムを検証できるようにしています。
Tarの--atime-preserve
オプションは、<paths>
のいずれかが読み取り専用のマウントされたデバイス上にある場合は、省略した方がよいでしょう。そうしないと、Tarがアクセスタイムスタンプを復元できなかった単一のファイルごとに警告が表示されます。書き込み可能<paths>
の場合、このオプションを使用して、ハッシュされたディレクトリのアクセスタイムスタンプを保持します。
Gillesの提案 ですでに使用されているTarオプション--no-recursion
は、Tarが単独でディレクトリに再帰的に下降するのを防ぎ、ソートされたfind
出力からフィードされたものに対してファイルごとに動作するのを防ぎます。
そして最後に、私がmd5sum
を使用することは事実ではありません。実際にはsha256sum
を使用します。
次のアプローチを使用して、ディレクトリの内容/属性が変更されたかどうかを判断します。
_cd /path/to/dir; ls -lnAR --time-style=+%s . | md5sum
_
拡張属性の変更も追跡する場合:
_cd /path/to/dir; (ls -lnAR --time-style=+%s .; getfacl -Rns .) | md5sum
_
通常の状況では、ファイルの変更時間はファイルの内容の変更をすでに示しているため、ファイルの内容を確認する必要はありません。これにより、操作全体が高速化されます。
ノート:
それでもファイルの内容を確認する場合:_tar c -C /path/to/dir | md5sum
_または拡張属性:cd /path/to/dir; (tar c .; getfacl -Rns .) | md5sum
幅広いシステムで機能させるために、ロケールを設定し、awkを使用してls出力からハードリンク数とディレクトリサイズを除外する必要がありました。
_cd /path;LC_ALL=C ls -lnAR --time-style=+%s .|awk '{$2=0;$1~/^d/&&$5=0;print}'|md5sum
_
これは私が頭の上に持っているものであり、これに実際に取り組んでいる時間を費やした人なら誰でも他の問題やコーナーケースを捕まえたでしょう。
ここにツールがあります(免責事項:私はそれに貢献しています) 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
Md5が必要ない場合は、試すことができます
find . -type f | xargs cksum | cksum