【问题标题】:How can I calculate an MD5 checksum of a directory?如何计算目录的 MD5 校验和?
【发布时间】:2010-12-12 01:11:31
【问题描述】:

我需要为某个目录和所有子目录下的特定类型的所有文件(例如*.py)计算一个汇总 MD5 校验和。

最好的方法是什么?


建议的解决方案非常好,但这并不是我所需要的。我正在寻找一种解决方案来获得 single summary 校验和,它将唯一地标识整个目录 - 包括其所有子目录的内容。

【问题讨论】:

  • 查看thisthis以获得更详细的解释。
  • 对我来说似乎是一个超级用户的问题。
  • 请注意,校验和不能唯一地识别任何东西。
  • 为什么你会有两个目录树,它们可能是也可能不是你想要唯一标识的“相同”?文件创建/修改/访问时间重要吗?版本控制是您真正需要的吗?
  • 在我的情况下真正重要的是整个目录树内容的相似性,这意味着 AFAIK 如下:1)目录树下任何文件的内容都没有改变 2)没有添加新文件到目录树 3) 没有文件被删除

标签: linux directory md5sum


【解决方案1】:

即时创建一个 tar 存档文件并将其通过管道传输到 md5sum

tar c dir | md5sum

这会生成一个单一的 MD5 哈希值,该值对于您的文件和子目录设置应该是唯一的。没有在磁盘上创建文件。

【讨论】:

  • @CharlesB 用一个校验和你永远不知道哪个文件不同。问题是关于目录的单个校验和。
  • ls -alR dir | md5sum 。这甚至更好,没有压缩只是读取。它是独一无二的,因为内容包含修改时间和文件大小;)
  • @Daps0l - 我的命令中没有压缩。您需要为 gzip 添加z,或为 bzip2 添加j。我都没做过。
  • 注意这样做会将文件的时间戳和其他内容整合到校验和计算中,而不仅仅是文件的内容
  • 这很可爱,但它并没有真正起作用。不能保证tar两次或在两台不同的计算机上使用同一组文件会产生完全相同的结果。
【解决方案2】:
find /path/to/dir/ -type f -name "*.py" -exec md5sum {} + | awk '{print $1}' | sort | md5sum

find 命令列出所有以 .py 结尾的文件。 为每个 .py 文件计算 MD5 哈希值。 AWK 用于挑选 MD5 哈希值(忽略文件名,文件名可能不是唯一的)。 对 MD5 哈希值进行排序。然后返回这个排序列表的 MD5 哈希值。

我已经通过复制测试目录对此进行了测试:

rsync -a ~/pybin/ ~/pybin2/

我重命名了~/pybin2中的一些文件。

find...md5sum 命令为两个目录返回相同的输出。

2bcf49a4d19ef9abd284311108d626f1  -

【讨论】:

  • 请注意,如果文件被重命名,将生成相同的校验和。因此,如果您考虑签名的文件布局部分,这并不真正适合“将唯一标识整个目录的校验和”。
  • 您可以稍微更改命令行以在每个文件校验和前面加上文件名(或者更好的是,文件的相对路径从 /path/to/dir/)所以它是在最终校验和中考虑。
  • @zim2001:是的,它可以改变,但据我了解这个问题(特别是由于 OP 在问题下的评论),如果 内容无论文件名或相对路径如何,文件的内容都是相同的。
  • @unutbu :我知道;我正在对 Valentin Milea 的上一个注释做出反应。
  • @ValentinMilea 如果您考虑签名的布局部分,只需删除 awk ... 部分。
【解决方案3】:

使用tar c <dir>ire_and_curses's suggestion有一些问题:

  • tar 按照它们在文件系统中存储的顺序处理目录条目,并且无法更改此顺序。如果你在不同的地方有“相同”的目录,这实际上会产生完全不同的结果,我不知道有什么办法可以解决这个问题(tar 不能按特定顺序“排序”它的输入文件)。
  • 我通常关心groupid和ownerid编号是否相同,不一定是group/owner的字符串表示是否相同。这与 rsync -a --delete 所做的一致:它几乎同步所有内容(减去 xattrs 和 acls),但它将根据所有者和组的 ID 同步所有者和组,而不是根据字符串表示。因此,如果您同步到不一定具有相同用户/组的不同系统,则应将 --numeric-owner 标志添加到 tar
  • tar 将包含您正在检查的目录的文件名,这只是需要注意的事项。

只要没有解决第一个问题(或者除非你确定它不会影响你),我就不会使用这种方法。

建议的基于find 的解决方案也不好,因为它们只包含文件,而不是目录,如果您在校验和时应记住空目录,这将成为一个问题。

最后,大多数建议的解决方案排序不一致,因为不同系统的排序规则可能不同。

这是我想出的解决方案:

dir=<mydir>; (find "$dir" -type f -exec md5sum {} +; find "$dir" -type d) | LC_ALL=C sort | md5sum

关于此解决方案的说明:

  • LC_ALL=C 用于确保跨系统的可靠排序顺序
  • 这并不能区分目录“named\nwithanewline”和两个目录“named”和“withanewline”,但这种情况发生的可能性似乎很小。人们通常使用-print0 标记find 来解决此问题,但由于这里还有其他事情,我只能看到会使命令变得比其价值更复杂的解决方案。

PS:我的一个系统使用有限的busybox find,它不支持-exec 也不支持-print0 标志,并且它附加'/'来表示目录,而findutils find 似乎不支持,所以对于这台机器我需要运行:

dir=<mydir>; (find "$dir" -type f | while read f; do md5sum "$f"; done; find "$dir" -type d | sed 's#/$##') | LC_ALL=C sort | md5sum

幸运的是,我没有名称中包含换行符的文件/目录,因此在该系统上这不是问题。

【讨论】:

  • +1:非常有趣!您是说不同文件系统类型之间或同一文件系统内的顺序可能不同?
  • 两者。它仅取决于每个目录中目录条目的顺序。 AFAIK 目录条目(在文件系统中)只是按照您“在目录中创建文件”的顺序创建的。一个简单的例子: $ mkdir a;触摸 a/file-1;触摸 a/file-2 $ mkdir b;触摸 b/file-2; touch b/file-1 $ (cd a; tar -c . | md5sum) fb29e7af140aeea5a2647974f7cdec77 - $ (cd b; tar -c . | md5sum) a3a39358158a87059b9f111ccffa1023 -
  • 我宁愿用普通的 xargs 替换 while-stuff,以便可以使用 -P 进行并行处理。这还需要对第二列进行额外的排序步骤,因为并行 md5sum 没有可重复的顺序。 find "$dir" -type f -print0 | xargs -P 6 -r0 md5sum | sort -k2
【解决方案4】:

如果你只关心文件而不关心空目录,这很好用:

find /path -type f | sort -u | xargs cat | md5sum

【讨论】:

  • 为什么需要cat?它适用于名称中带有空格的文件吗?
  • 好的,tesujimath 似乎已经离开了大楼(“上次看到是在 2 年前”)。也许其他人可以敲响它?
  • 如果你不cat 输入到md5sum 的文件将是find 的输出,这是一个文件名(和路径)列表不是 这些文件的内容。
  • 注意:我考虑过省略sort -u,但我们需要它,因为否则文件的顺序可能不同,因此校验和也是如此。
【解决方案5】:

为了完整起见,这里有md5deep(1);由于 *.py 过滤器要求,它不能直接适用,但应该与 find(1) 一起使用。

【讨论】:

  • 如果我只想计算一个目录的md5校验和,我会使用什么参数?
  • 它应该做什么?你能在你的答案中elaborate 吗(没有详细说明,这只不过是一个仅链接的答案)? (但没有“编辑:”、“更新:”或类似的 - 问题/答案应该看起来好像是今天写的。)
【解决方案6】:

最适合我的解决方案:

find "$path" -type f -print0 | sort -z | xargs -r0 md5sum | md5sum

为什么它最适合我:

  1. 处理包含空格的文件名
  2. 忽略文件系统元数据
  3. 检测文件是否已重命名

其他答案的问题:

文件系统元数据不会被忽略:

tar c - "$path" | md5sum

不处理包含空格的文件名,也不检测文件是否被重命名:

find /path -type f | sort -u | xargs cat | md5sum

【讨论】:

    【解决方案7】:

    如果你想要一个跨越整个目录的 MD5 哈希值,我会这样做

    cat *.py | md5sum
    

    【讨论】:

    • 对于子目录使用类似cat **.py | md5sum
    【解决方案8】:

    校验所有文件,包括内容和文件名

    grep -ar -e . /your/dir | md5sum | cut -c-32
    

    同上,但只包括 *.py 文件

    grep -ar -e . --include="*.py" /your/dir | md5sum | cut -c-32
    

    如果你愿意,你也可以关注符号链接

    grep -aR -e . /your/dir | md5sum | cut -c-32
    

    您可以考虑与 grep 一起使用的其他选项

    -s, --no-messages         suppress error messages
    -D, --devices=ACTION      how to handle devices, FIFOs and sockets;
    -Z, --null                print 0 byte after FILE name
    -U, --binary              do not strip CR characters at EOL (MSDOS/Windows)
    

    【讨论】:

    【解决方案9】:

    GNU 查找

    find /path -type f -name "*.py" -exec md5sum "{}" +;
    

    【讨论】:

    • 最后一个token应该是\;吗?
    【解决方案10】:

    从技术上讲,您只需要运行 ls -lR *.py | md5sum。除非您担心有人修改文件并将它们恢复到原始日期并且从不更改文件的大小,否则ls 的输出应该会告诉您文件是否已更改。我的 unix-foo 很弱,因此您可能需要更多命令行参数来获取打印的创建时间和修改时间。 ls 还会告诉您文件的权限是否已更改(如果您不在乎,我相信有开关可以关闭它)。

    【讨论】:

    • 这可能适合某些用例,但通常您希望校验和仅反映内容而不是日期。例如,如果我touch 一个文件更改其日期(但不是其内容),那么我希望校验和保持不变。
    【解决方案11】:

    使用md5deep

    md5deep -r FOLDER | awk '{print $1}' | sort | md5sum

    【讨论】:

    • 它应该做什么?工作原理是什么?为什么它有效?你能elaborate回答吗? (但没有“编辑:”、“更新:”或类似的 - 问题/答案应该看起来好像是今天写的。)
    • 好的,doesntreallymatter 似乎已经离开了大楼(“最后一次出现在 7 年前”)。也许其他人可以敲响它?
    【解决方案12】:

    md5sum 对我来说工作得很好,但我遇到了sort 和排序文件名的问题。所以我改为按md5sum 结果排序。我还需要排除一些文件以创建可比较的结果。

    find . -type f -print0 \ | xargs -r0 md5sum \ | grep -v ".env" \ | grep -v "vendor/autoload.php" \ | grep -v "vendor/composer/" \ | sort -d \ | md5sum

    【讨论】:

      【解决方案13】:

      我遇到了同样的问题,所以我想出了这个脚本,它只列出目录中文件的 MD5 哈希值,如果它找到一个子目录,它会从那里再次运行,为此脚本必须能够如果在 $1 中传递了所述参数,则遍历当前目录或从子目录运行

      #!/bin/bash
      
      if [ -z "$1" ] ; then
      
      # loop in current dir
      ls | while read line; do
        ecriv=`pwd`"/"$line
      if [ -f $ecriv ] ; then
          md5sum "$ecriv"
      elif [ -d $ecriv ] ; then
          sh myScript "$line" # call this script again
      fi
      
      done
      
      
      else # if a directory is specified in argument $1
      
      ls "$1" | while read line; do
        ecriv=`pwd`"/$1/"$line
      
      if [ -f $ecriv ] ; then
          md5sum "$ecriv"
      
      elif [ -d $ecriv ] ; then
          sh myScript "$line"
      fi
      
      done
      
      
      fi
      

      【讨论】:

      • 我很确定如果文件名包含空格或引号,此脚本将失败。我觉得 bash 脚本很烦人,但我要做的是更改 IFS。
      【解决方案14】:

      如果您想真正独立于文件系统属性和某些tar 版本的位级差异,您可以使用cpio

      cpio -i -e theDirname | md5sum
      

      【讨论】:

        【解决方案15】:

        我想补充一点,如果您尝试对 Git 存储库中的文件/目录执行此操作以跟踪它们是否已更改,那么这是最好的方法:

        git log -1 --format=format:%H --full-diff <file_or_dir_name>
        

        如果它不是 Git 目录/存储库,那么 the answer by ire_and_curses 可能是最好的选择:

        tar c <dir_name> | md5sum
        

        但是,请注意tar 命令如果您在不同的操作系统和其他东西中运行它会改变输出哈希。如果您想对此免疫,这是最好的方法,尽管乍一看它看起来不是很优雅:

        find <dir_name> -type f -print0 | sort -z | xargs -0 md5sum | md5sum | awk '{ print $1 }'
        

        【讨论】:

        • tar c | md5sum,这是理想的解决方案。
        【解决方案16】:

        还有两种解决方案:

        创建:

        du -csxb /path | md5sum > file
        
        ls -alR -I dev -I run -I sys -I tmp -I proc /path | md5sum > /tmp/file
        

        检查:

        du -csxb /path | md5sum -c file
        
        ls -alR -I dev -I run -I sys -I tmp -I proc /path | md5sum -c /tmp/file
        

        【讨论】:

          猜你喜欢
          • 2012-02-28
          • 1970-01-01
          • 2011-05-25
          • 2012-05-18
          • 2017-04-07
          • 2016-04-02
          • 1970-01-01
          相关资源
          最近更新 更多