213 directory hashsum checksum
md5sum 程序不提供目录的校验和。我想为目录的整个内容(包括子目录中的文件)获取单个 MD5 校验和。也就是说,由所有文件组成的一个组合校验和。有没有办法做到这一点?
War*_*ung 253
正确的方法取决于你问的确切原因:
如果您只需要树文件内容的散列,这将起作用:
$ find -s somedir -type f -exec md5sum {} \; | md5sum
Run Code Online (Sandbox Code Playgroud)
这首先以可预测的顺序单独汇总所有文件内容,然后传递该文件名列表和 MD5 散列值以自行散列,给出一个值,该值仅在树中一个文件的内容更改时更改。
不幸的是,find -s只适用于 BSD find(1),在 macOS、FreeBSD、NetBSD 和 OpenBSD 中使用。要在具有 GNU 或 SUS find(1) 的系统上获得类似的东西,您需要更丑陋的东西:
$ find somedir -type f -exec md5sum {} \; | sort -k 2 | md5sum
Run Code Online (Sandbox Code Playgroud)
我们find -s通过添加对sort. 该-k 2位告诉它跳过 MD5 散列,因此它只对文件名进行排序,这些文件名在字段 2 到行尾中按sort's 计算。
这个版本的命令有一个弱点,那就是如果你有任何包含换行符的文件名,它很容易变得混乱,因为它看起来像多行sort调用。该find -s变体不存在这样的问题,因为树的遍历和排序在同一程序中发生的,find。
在任何一种情况下,排序都是必要的,以避免误报:最常见的 Unix/Linux 文件系统不会以稳定、可预测的顺序维护目录列表。您可能不会通过使用ls等来意识到这一点,它会为您默默地对目录内容进行排序。find不以某种方式对其输出进行排序的调用将导致输出中的行顺序与底层文件系统返回它们的任何顺序相匹配,如果作为输入提供给它的文件顺序发生变化,这将导致此命令给出更改的哈希值,即使数据保持相同。
您很可能会问上面-k 2的 GNUsort命令中的位是否是必需的。鉴于文件数据的哈希值是文件名的适当代理,只要内容没有改变,如果我们删除此选项,我们将不会得到误报,允许我们对 GNU 和 BSD 使用相同的命令sort。但是,要意识到文件名的确切顺序有很小的可能性(MD5 为1:2 128)与不匹配的部分顺序不匹配,-k 2如果发生哈希冲突,则不匹配。但是请记住,如果这种不匹配的小概率对您的应用程序很重要,那么整个方法对您来说可能是不可能的。
您可能需要将md5sum命令更改为md5或其他一些散列函数。如果您选择另一个散列函数并需要为您的系统使用第二种形式的命令,您可能需要相应地调整sort命令。另一个陷阱是一些数据求和程序根本不写出文件名,一个典型的例子是旧的 Unixsum程序。
这种方法有点低效,调用md5sumN+1 次,其中 N 是树中的文件数,但这是避免散列文件和目录元数据的必要成本。
如果您需要能够检测到树中的任何内容已更改,而不仅仅是文件内容,请要求tar为您打包目录内容,然后将其发送到md5sum:
$ tar -cf - somedir | md5sum
Run Code Online (Sandbox Code Playgroud)
因为tar还可以看到文件权限、所有权等,这也将检测对这些内容的更改,而不仅仅是对文件内容的更改。
这种方法要快得多,因为它只遍历树一次,并且只运行一次散列程序。
与find上面的基于方法一样,tar将按照底层文件系统返回它们的顺序处理文件名。很可能在您的应用程序中,您可以确定不会导致这种情况发生。我可以想到至少三种不同的使用模式,可能是这种情况。(我不打算列出它们,因为我们进入了未指定的行为领域。这里的每个文件系统都可能不同,甚至从操作系统的一个版本到下一个版本也是如此。)
如果您发现自己收到误报,我建议您find | cpio选择Gilles 的回答中的选项。
Gil*_*il' 45
校验和需要是文件作为字符串的确定性和明确表示。确定性意味着如果您将相同的文件放在相同的位置,您将获得相同的结果。明确意味着两组不同的文件具有不同的表示。
制作包含文件的存档是一个好的开始。这是一个明确的表示(显然,因为您可以通过提取存档来恢复文件)。它可能包括文件元数据,例如日期和所有权。然而,这还不完全正确:存档是模棱两可的,因为它的表示取决于文件的存储顺序,如果适用于压缩。
一种解决方案是在存档之前对文件名进行排序。如果您的文件名不包含换行符,您可以运行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
Run Code Online (Sandbox Code Playgroud)
如果您只想考虑文件数据而不考虑元数据,您可以制作一个仅包含文件内容的存档,但没有标准工具可以做到这一点。您可以包含文件的哈希值,而不是包含文件内容。如果文件名不包含换行符,并且只有常规文件和目录(没有符号链接或特殊文件),这很容易,但您需要注意以下几点:
{ 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
Run Code Online (Sandbox Code Playgroud)
除了校验和列表之外,我们还包括一个目录列表,否则空目录将不可见。文件列表已排序(在特定的、可重现的语言环境中——感谢 Peter.O 提醒我这一点)。echo将两部分分开(没有这个,你可以创建一些空目录,其名称看起来像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
Run Code Online (Sandbox Code Playgroud)
这是一个经过最少测试的 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()
Run Code Online (Sandbox Code Playgroud)
Dee*_*tal 17
如果您的目标只是找出两个目录之间的差异,请考虑使用 diff。
尝试这个:
diff -qr dir1 dir2
Run Code Online (Sandbox Code Playgroud)
小智 15
看看md5deep。您可能会感兴趣的 md5deep 的一些功能:
递归操作 - md5deep 能够递归检查整个目录树。也就是说,为目录中的每个文件和每个子目录中的每个文件计算 MD5。
比较模式 - md5deep 可以接受已知哈希列表并将它们与一组输入文件进行比较。该程序可以显示那些与已知散列列表匹配或不匹配的输入文件。
...
Dmi*_*nov 10
使用checksumdir:
$ pip install checksumdir
$ checksumdir -a md5 assets/js
981ac0bc890de594a9f2f40e00f13872
$ checksumdir -a sha1 assets/js
88cd20f115e31a1e1ae381f7291d0c8cd3b92fad
Run Code Online (Sandbox Code Playgroud)
更快和更容易比其他的bash解决方案。
您可以递归地散列每个文件,然后散列结果文本:
> md5deep -r -l . | sort | md5sum
d43417958e47758c6405b5098f151074 *-
Run Code Online (Sandbox Code Playgroud)
md5deep是必需的。