我正在编写一个 shell 脚本,使用它shasum
来检查目录的内容是否已更改。
在 Linux 和 FreeBSD 上,shasum
当我这样做时,它们有相同的行为shasum <directory>
,但是在 MacOS 上,它们shasum
给我哈希值仅适用于文件。
FreeBSD
$ shasum CONTENTS/
7f986e5e5289c59db1bba48df92ffe4707830aaa CONTENTS/
Linux
$ shasum CONTENTS/
7f986e5e5289c59db1bba48df92ffe4707830aaa CONTENTS/
苹果系统
$ shasum CONTENTS/
shasum: CONTENTS/:
我怎样才能计算 MacOS 中目录的哈希值?
尝试 1:使用 TAR 和管道
尝试使用但似乎这个 tar 选项在 MacOS 上不起作用。
tar cO CONTENTS/ | shasum
tar: Option -O is not permitted in mode -c
da39a3ee5e6b4b0d3255bfef95601890afd80709 -
尝试 2:使用 FIND/EXEC
MacOS 和 FreeBSD 之间是一致的,但 Linux 返回了一个奇怪的哈希值
find CONTENTS -type f -exec shasum {} \; | sort -k 2 | shasum
Linux
c2ddb9bc5f543e956f5cdcc76750cb78cc5f26f3
FreeBSD
3ac2a9d4e2fc5d2d2ec3c7f612e680990cc35824
苹果系统
3ac2a9d4e2fc5d2d2ec3c7f612e680990cc35824
关于 TAR 的其他发现
tar
非常棒,因为它可以“存档”一个文件夹,然后我就可以这样做了,但是文件夹结构的“遍历”shasum
顺序是tar
跨操作系统不一致。正如一些助手在评论中提到的,我应该tar
在所有系统中使用相同版本。
仅举一个例子,在系统 1 上我有这个命令:
drwxr-xr-x 0 root wheel 0 27 Jul 07:23 usr/
-rw-r--r-- 0 root wheel 0 27 Jul 07:25 usr/aaa
drwxr-xr-x 0 root wheel 0 27 Jul 07:25 usr/f1/
-rw-r--r-- 0 root wheel 0 27 Jul 07:25 usr/f1/aaa
drwxr-xr-x 0 root wheel 0 27 Jul 07:25 usr/f1/f0/
-rw-r--r-- 0 root wheel 0 27 Jul 07:25 usr/f1/f0/aaa
drwxr-xr-x 0 root wheel 0 27 Jul 07:25 usr/f2/
-rw-r--r-- 0 root wheel 0 27 Jul 07:25 usr/f2/aaa
drwxr-xr-x 0 root wheel 0 27 Jul 07:25 usr/f2/f1/
-rw-r--r-- 0 root wheel 0 27 Jul 07:25 usr/f2/f1/aaa
drwxr-xr-x 0 root wheel 0 27 Jul 07:25 usr/f2/f1/f0/
-rw-r--r-- 0 root wheel 0 27 Jul 07:25 usr/f2/f1/f0/aaa
drwxr-xr-x 0 root wheel 0 27 Jul 07:25 usr/f3/
-rw-r--r-- 0 root wheel 0 27 Jul 07:25 usr/f3/aaa
drwxr-xr-x 0 root wheel 0 27 Jul 07:25 usr/f3/f2/
-rw-r--r-- 0 root wheel 0 27 Jul 07:25 usr/f3/f2/aaa
drwxr-xr-x 0 root wheel 0 27 Jul 07:25 usr/f3/f2/f1/
-rw-r--r-- 0 root wheel 0 27 Jul 07:25 usr/f3/f2/f1/aaa
在系统 2 上我有以下顺序:
drwxr-xr-x 0 root wheel 0 27 Jul 07:23 usr/
-rw-r--r-- 0 root wheel 0 27 Jul 07:25 usr/aaa
drwxr-xr-x 0 root wheel 0 27 Jul 07:25 usr/f1/
drwxr-xr-x 0 root wheel 0 27 Jul 07:25 usr/f2/
drwxr-xr-x 0 root wheel 0 27 Jul 07:25 usr/f3/
-rw-r--r-- 0 root wheel 0 27 Jul 07:25 usr/f3/aaa
drwxr-xr-x 0 root wheel 0 27 Jul 07:25 usr/f3/f2/
-rw-r--r-- 0 root wheel 0 27 Jul 07:25 usr/f3/f2/aaa
drwxr-xr-x 0 root wheel 0 27 Jul 07:25 usr/f3/f2/f1/
-rw-r--r-- 0 root wheel 0 27 Jul 07:25 usr/f3/f2/f1/aaa
-rw-r--r-- 0 root wheel 0 27 Jul 07:25 usr/f2/aaa
drwxr-xr-x 0 root wheel 0 27 Jul 07:25 usr/f2/f1/
-rw-r--r-- 0 root wheel 0 27 Jul 07:25 usr/f2/f1/aaa
drwxr-xr-x 0 root wheel 0 27 Jul 07:25 usr/f2/f1/f0/
-rw-r--r-- 0 root wheel 0 27 Jul 07:25 usr/f2/f1/f0/aaa
-rw-r--r-- 0 root wheel 0 27 Jul 07:25 usr/f1/aaa
drwxr-xr-x 0 root wheel 0 27 Jul 07:25 usr/f1/f0/
-rw-r--r-- 0 root wheel 0 27 Jul 07:25 usr/f1/f0/aaa
从某个tar
角度来看,这一切都很好,但由于顺序,shasum
产生了不同的哈希值。
结论
shasum
在 Linux 和 BSD 中检查单个文件哈希值是一致的,但是,当涉及到目录时,一致性只发生在 MacOS 和 FreeBSD 上,这可能是由于文件的排序方式造成的。
如果使用命令强制排序find
,则只能在 FreeBSD 和 MacOS 中获得一致性,但是这种方法耗时较长,因为需要花费大量时间来计算每个文件的哈希值,然后计算整个结构的哈希值。
使用tar
创建临时文件然后执行的操作shasum
也发现 Linux 和 BSD 之间不一致,也许是因为存档方法不同。
我认为唯一的出路就是重新设计我的解决方案。
答案1
mtree
就是您想要的工具。
认为:
$ mkdir foo
$ date > foo/date1; sleep 3
$ date > foo/date2; sleep 3
$ date > foo/date3
$ grep . foo/*
foo/date1:Wed Jul 24 16:11:32 PDT 2019
foo/date2:Wed Jul 24 16:11:35 PDT 2019
foo/date3:Wed Jul 24 16:11:38 PDT 2019
$ find . -ls
7318841 0 drwxr-xr-x 3 admin staff 102 Jul 24 16:11 .
7318847 0 drwxr-xr-x 5 admin staff 170 Jul 24 16:11 ./foo
7318849 8 -rw-r--r-- 1 admin staff 29 Jul 24 16:11 ./foo/date1
7318851 8 -rw-r--r-- 1 admin staff 29 Jul 24 16:11 ./foo/date2
7318853 8 -rw-r--r-- 1 admin staff 29 Jul 24 16:11 ./foo/date3
创建目录的参考清单foo
并将其存储在foo.mtree
:
$ mtree -c -K sha256digest -p foo > foo.mtree
现在去处理该目录中的任何文件。
$ touch foo/date3
再次运行mtree
并传递您之前创建的清单,它将mtree
告诉您发生了哪些变化:
$ mtree -p foo < foo.mtree || echo fail
date3 changed
modification time expected Wed Jul 24 16:11:38 2019 found Wed Jul 24 16:14:00 2019
fail
$ echo '$ date > foo/date2' >> bar
$ mtree -p foo < foo.mtree || echo fail
date2 changed
modification time expected Wed Jul 24 16:11:35 2019 found Wed Jul 24 16:19:40 2019
SHA-256 expected c76a568f08d98c2830f2fdfb42415c3ec15341b8741450d4bbd863f1d5c4c691 found ddcf8d07785bfe4d031a989339835dc3b8b44653019568dcee612c44fc8e2f70
date3 changed
modification time expected Wed Jul 24 16:11:38 2019 found Wed Jul 24 16:14:00 2019
fail
foo
自清单创建以来缺少或添加的任何文件也将被报告:
$ mv foo/date1 foo/date4
$ mtree -p foo < foo.mtree || echo fail
. changed
modification time expected Wed Jul 24 16:11:38 2019 found Wed Jul 24 16:21:38 2019
date2 changed
modification time expected Wed Jul 24 16:11:35 2019 found Wed Jul 24 16:19:40 2019
SHA-256 expected c76a568f08d98c2830f2fdfb42415c3ec15341b8741450d4bbd863f1d5c4c691 found ddcf8d07785bfe4d031a989339835dc3b8b44653019568dcee612c44fc8e2f70
date3 changed
modification time expected Wed Jul 24 16:11:38 2019 found Wed Jul 24 16:14:00 2019
date4 extra
./date1 missing
fail
答案2
林特将会做(我认为是)你想要做的事。
相关要点:
- 它默认不使用 SHA,但可以告知它使用。
- 它可以通过 homebrew 安装在 MacOS 上。
- 默认情况下,它不会计算单个指定目录的校验和。可以指示它从给定的起点计算所有目录的校验和,以此作为查找该点以下“重复”目录的方法。但作为副作用,它也会执行您似乎要求的操作。
- 对于您所寻找的东西来说,它可能有些过度,而且可能需要花费一些时间才能找出最佳的选项标志,但它相当强大。
- 弄清楚要使用哪些标志可能很棘手。获取目录校验和很容易,但要不是做其他事情,可能会很棘手。(虽然要清楚,它实际上并没有修改任何东西。最多,它会生成一个 shell 脚本,您可以稍后手动运行该脚本,以根据需要修改内容。您似乎需要的是 JSON 和/或 CSV 输出文件,它们将为您提供您正在寻找的目录校验和。)
我在 Bash 脚本中使用 rmlint 来查找重复的目录。下面这个命令会尽量少做你想做的事情,尽可能少做其他事情:
rmlint "base/dir/to/start/from" --see-symlinks --hidden --algorithm=sha256 --types=none,duplicatedirs --no-backup -o csv:log.csv