Linux:计算给定文件夹的单个哈希值&内容?

时间:2009-02-13 09:51:41

标签: linux bash hash

当然必须有办法轻松做到这一点!

我尝试了sha1summd5sum等Linux命令行应用程序,但它们似乎只能计算单个文件的哈希值并输出一个哈希值列表,每个哈希值一个文件。

我需要为文件夹的整个内容生成一个哈希值(而不仅仅是文件名)。

我想做点什么

sha1sum /folder/of/stuff > singlehashvalue

编辑澄清一下,我的文件位于目录树的多个级别,它们并非都位于同一根文件夹中。

17 个答案:

答案 0 :(得分:85)

一种可能的方式是:

sha1sum path/to/folder/* | sha1sum

如果有一个完整的目录树,那么最好使用find和xargs。一个可能的命令是

find path/to/folder -type f -print0 | sort -z | xargs -0 sha1sum | sha1sum

最后,如果您还需要考虑权限和空目录:

(find path/to/folder -type f -print0  | sort -z | xargs -0 sha1sum;
 find path/to/folder \( -type f -o -type d \) -print0 | sort -z | \
   xargs -0 stat -c '%n %a') \
| sha1sum

stat的参数将导致它打印文件名,后跟八进制权限。这两个发现将一个接一个地运行,导致磁盘IO的数量增加一倍,第一个查找所有文件名并校验内容,第二个查找所有文件和目录名,打印名称和模式。 “文件名和校验和”列表,后跟“具有权限的名称和目录”将进行校验和,以获得较小的校验和。

答案 1 :(得分:17)

  • 使用aide等文件系统入侵检测工具。

  • 哈希目录的tar球:

    tar cvf - /path/to/folder | sha1sum

  • 自己编写代码,例如vatine's oneliner

    find /path/to/folder -type f -print0 | sort -z | xargs -0 sha1sum | sha1sum

答案 2 :(得分:10)

您可以执行tar -c /path/to/folder | sha1sum

答案 3 :(得分:5)

如果您只是想检查文件夹中的某些内容是否发生了变化,我建议您选择以下内容:

ls -alR --full-time /folder/of/stuff | sha1sum

它只会给你一个ls输出的哈希值,它包含文件夹,子文件夹,它们的文件,它们的时间戳,大小和权限。几乎所有你需要确定的东西是否有变化。

请注意,此命令不会为每个文件生成哈希值,但这就是为什么它应该比使用find更快。

答案 4 :(得分:3)

如果您只想散列文件内容,忽略文件名,则可以使用

cat $FILES | md5sum

确保在计算哈希值时文件的顺序相同:

cat $(echo $FILES | sort) | md5sum

但是你的文件列表中没有目录。

答案 5 :(得分:2)

kvantour's answer 添加多处理和进度条

大约快 30 倍(取决于 CPU)

100%|██████████████████████████████████| 31378/31378 [03:03<00:00, 171.43file/s]
# to hash without permissions

find . -type f -print0 | sort -z | xargs -P $(nproc --all) -0 sha1sum | tqdm --unit file --total $(find . -type f | wc -l) | sha1sum | tee sha1sum_without_permissions
# to hash permissions

(find . -type f -print0  | sort -z | xargs -P $(nproc --all) -0 sha1sum; 
find . \( -type f -o -type d \) -print0 | sort -z | xargs -P $(nproc --all) -0 stat -c '%n %a') | tqdm --unit file --total $(find . -type f | wc -l) | sha1sum | tee sha1sum_permissions

确保安装了 tqdmpip install tqdm 或检查文档

答案 6 :(得分:2)

到目前为止,最快的方法仍然是使用tar。借助其他几个参数,我们还可以消除由元数据引起的差异。

要使用tar哈希目录,需要确保在tar期间对路径进行排序,否则它总是不同的。

tar -C <root-dir> -cf - --sort=name <dir> | sha256sum

忽略时间

如果您不关心访问时间或修改时间,还可以使用类似--mtime='UTC 2019-01-01' 的方法来确保所有时间戳都相同。

忽略所有权

通常,我们需要添加--group=0 --owner=0 --numeric-owner来统一所有者元数据。

忽略一些文件

使用--exclude=PATTERN

答案 7 :(得分:2)

有一个python脚本:

http://code.activestate.com/recipes/576973-getting-the-sha-1-or-md5-hash-of-a-directory/

如果更改文件名而不更改其字母顺序,则哈希脚本将不会检测到它。但是,如果您更改文件的顺序或任何文件的内容,运行该脚本将为您提供与以前不同的哈希。

答案 8 :(得分:1)

实现这一目标的另一个工具:

http://md5deep.sourceforge.net/

听起来像:md5sum,但也是递归的,加上其他功能。

答案 9 :(得分:1)

我编写了一个Groovy脚本来执行此操作:

import java.security.MessageDigest

public static String generateDigest(File file, String digest, int paddedLength){
    MessageDigest md = MessageDigest.getInstance(digest)
    md.reset()
    def files = []
    def directories = []

    if(file.isDirectory()){
        file.eachFileRecurse(){sf ->
            if(sf.isFile()){
                files.add(sf)
            }
            else{
                directories.add(file.toURI().relativize(sf.toURI()).toString())
            }
        }
    }
    else if(file.isFile()){
        files.add(file)
    }

    files.sort({a, b -> return a.getAbsolutePath() <=> b.getAbsolutePath()})
    directories.sort()

    files.each(){f ->
        println file.toURI().relativize(f.toURI()).toString()
        f.withInputStream(){is ->
            byte[] buffer = new byte[8192]
            int read = 0
            while((read = is.read(buffer)) > 0){
                md.update(buffer, 0, read)
            }
        }
    }

    directories.each(){d ->
        println d
        md.update(d.getBytes())
    }

    byte[] digestBytes = md.digest()
    BigInteger bigInt = new BigInteger(1, digestBytes)
    return bigInt.toString(16).padLeft(paddedLength, '0')
}

println "\n${generateDigest(new File(args[0]), 'SHA-256', 64)}"

您可以自定义用法以避免打印每个文件,更改消息摘要,取出目录哈希等。我已根据NIST测试数据对其进行了测试,并且按预期工作。 http://www.nsrl.nist.gov/testdata/

gary-macbook:Scripts garypaduana$ groovy dirHash.groovy /Users/garypaduana/.config
.DS_Store
configstore/bower-github.yml
configstore/insight-bower.json
configstore/update-notifier-bower.json
filezilla/filezilla.xml
filezilla/layout.xml
filezilla/lockfile
filezilla/queue.sqlite3
filezilla/recentservers.xml
filezilla/sitemanager.xml
gtk-2.0/gtkfilechooser.ini
a/
configstore/
filezilla/
gtk-2.0/
lftp/
menus/
menus/applications-merged/

79de5e583734ca40ff651a3d9a54d106b52e94f1f8c2cd7133ca3bbddc0c6758

答案 10 :(得分:1)

我会通过sort将各个文件的结果通过md5sum(以防止仅仅重新排序文件以更改哈希值)到sha1sum或{{1}},无论您选择哪个。

答案 11 :(得分:0)

强大而干净的方法

  • 首先,不要占用可用内存!以块的形式散列文件而不是整个文件。
  • 针对不同需求/目的的不同方法(以下全部或选择适用的方式):
    • 仅散列目录树中所有条目的条目名称
    • 散列所有条目的文件内容(保留元数据,inode数,ctime,atime,mtime,size等等,你明白了)
    • 对于符号链接,其内容是引用名称。哈希或选择跳过
    • 在散列条目内容时跟随或不遵循(已解析的名称)符号链接
    • 如果它是一个目录,其内容只是目录条目。在递归遍历时,它们最终会被散列,但是该级别的目录条目名称是否应该进行哈希以标记此目录?在需要哈希以快速识别变化而无需深入遍历以散列内容的用例中是有帮助的。一个例子是文件的名称更改,但其余内容保持不变,它们都是相当大的文件
    • 处理大文件(再次,记住RAM)
    • 处理非常深的目录树(请注意打开的文件描述符)
    • 处理非标准文件名
    • 如何处理套接字,管道/ FIFO,块设备,字符设备等文件?还必须哈希它们吗?
    • 在遍历时不要更新任何条目的访问时间,因为这对于某些用例来说会产生副作用并且适得其反(直观?)。

这就是我的头脑,任何花费一些时间在这方面工作的人都会抓住其他陷阱和角落案件。

Here's a tool,内存非常清晰,可以解决大多数情况,边缘可能有点粗糙,但非常有帮助。

dtreetrawl

的示例用法和输出
Usage:
  dtreetrawl [OPTION...] "/trawl/me" [path2,...]

Help Options:
  -h, --help                Show help options

Application Options:
  -t, --terse               Produce a terse output; parsable.
  -j, --json                Output as JSON
  -d, --delim=:             Character or string delimiter/separator for terse output(default ':')
  -l, --max-level=N         Do not traverse tree beyond N level(s)
  --hash                    Enable hashing(default is MD5).
  -c, --checksum=md5        Valid hashing algorithms: md5, sha1, sha256, sha512.
  -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
  -s, --hash-symlink        Include symbolic links' referent name while calculating the root checksum
  -e, --hash-dirent         Include hash of directory entries while calculating root checksum

人性化输出的片段:

...
... //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

答案 12 :(得分:0)

我必须检查整个目录以查找文件。

但排除时间戳,目录所有权。

目标是在任何地方获得相同的总和,如果文件相同的话。

包括托管到其他计算机,除了文件之外的任何内容,或者对它们进行更改。

md5sum * | md5sum | cut -d' ' -f1

它生成一个hash by file列表,然后将这些哈希值连接成一个。

这比tar方法快。

对于哈希中的更强大的隐私,我们可以在同一个食谱上使用 sha512sum

sha512sum * | sha512sum | cut -d' ' -f1

使用 sha512sum ,哈希在任何地方都是相同的,但是没有已知的方法来反转它。

答案 13 :(得分:0)

这是Python 3中的一个简单,简短的变体,适用于小型文件(例如源树或其他东西,其中每个文件可以单独容纳RAM),忽略空目录,基于另一个的想法解决方案:

import os, hashlib

def hash_for_directory(path, hashfunc=hashlib.sha1):                                                                                            
    filenames = sorted(os.path.join(dp, fn) for dp, _, fns in os.walk(path) for fn in fns)         
    index = '\n'.join('{}={}'.format(os.path.relpath(fn, path), hashfunc(open(fn, 'rb').read()).hexdigest()) for fn in filenames)               
    return hashfunc(index.encode('utf-8')).hexdigest()                          

它的工作原理如下:

  1. 以递归方式查找目录中的所有文件并按名称对其进行排序
  2. 计算每个文件的哈希值(默认值:SHA-1)(将整个文件读入内存)
  3. 使用“filename = hash”行创建文本索引
  4. 将该索引编码回UTF-8字节字符串并哈希
  5. 如果SHA-1不是你的一杯茶,你可以传递different hash function作为第二个参数。

答案 14 :(得分:0)

如果这是一个git仓库,并且您想忽略.gitignore中的任何文件,则可能要使用以下命令:

git ls-files <your_directory> | xargs sha256sum | cut -d" " -f1 | sha256sum | cut -d" " -f1

这对我来说很好。

答案 15 :(得分:0)

您可以sha1sum生成哈希值列表,然后再次列出sha1sum,这取决于您想要完成的内容。

答案 16 :(得分:0)

尝试分两步完成:

  1. 为文件夹中的所有文件创建包含哈希值的文件
  2. 哈希此文件
  3. 像这样:

    # for FILE in `find /folder/of/stuff -type f | sort`; do sha1sum $FILE >> hashes; done
    # sha1sum hashes
    

    或者一次完成所有操作:

    # cat `find /folder/of/stuff -type f | sort` | sha1sum