Linux:为给定文件夹计算单个哈希值&;目录

Linux:为给定文件夹计算单个哈希值&;目录,linux,bash,hash,Linux,Bash,Hash,当然,一定有办法轻松做到这一点 我尝试过Linux命令行应用程序,如sha1sum和md5sum,但它们似乎只能计算单个文件的哈希值并输出哈希值列表,每个文件一个 我需要为文件夹的全部内容(不仅仅是文件名)生成一个散列 我想做点像 sha1sum /folder/of/stuff > singlehashvalue 编辑:为了澄清,我的文件位于目录树中的多个级别,它们不都位于同一根文件夹中。如果您只想对文件内容进行哈希运算,而忽略文件名,则可以使用 cat $FILES | md5sum

当然,一定有办法轻松做到这一点

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

我需要为文件夹的全部内容(不仅仅是文件名)生成一个散列

我想做点像

sha1sum /folder/of/stuff > singlehashvalue

编辑:为了澄清,我的文件位于目录树中的多个级别,它们不都位于同一根文件夹中。

如果您只想对文件内容进行哈希运算,而忽略文件名,则可以使用

cat $FILES | md5sum
计算哈希时,请确保文件的顺序相同:

cat $(echo $FILES | sort) | md5sum

但您的文件列表中不能有目录。

请尝试分两步进行:

  • 为文件夹中的所有文件创建一个包含哈希的文件
  • 散列此文件
  • 像这样:

    # 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
    

    您可以
    sha1sum
    生成散列值列表,然后
    sha1sum
    再次生成该列表,这取决于您想要完成的具体操作。

    我将通过
    排序
    对单个文件的结果进行管道处理(以防止仅仅对文件进行重新排序以更改散列)进入
    md5sum
    sha1sum
    ,无论您选择哪个。

    一种可能的方法是: sha1sum path/to/folder/* | sha1sum
    stat
    的参数将使其打印文件名,后跟八进制权限。这两个查找将依次运行,导致磁盘IO量加倍,第一个查找所有文件名并对内容进行校验和,第二个查找所有文件名和目录名,打印名称和模式。然后将对“文件名和校验和”列表,后跟“名称和目录,具有权限”进行校验和,以获得较小的校验和。

    • 使用文件系统入侵检测工具,如

    • 散列目录的tar球:

      tar cvf-/path/to/folder|sha1sum

    • 自己编写代码,比如:

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


      • 您可以执行
        tar-c/path/to/folder | sha1sum
        有一个python脚本:


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

        另一个实现此目的的工具:

        原样听起来:像md5sum,但也是递归的,再加上其他特性


        md5deep-r{direcotory}

        我编写了一个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)}"
        

        如果您只想检查文件夹中的内容是否发生了更改,我建议您:

        ls -alR --full-time /folder/of/stuff | sha1sum
        
        它只会给您一个ls输出的散列,其中包含文件夹、子文件夹、它们的文件、时间戳、大小和权限。几乎所有你需要确定的事情都发生了变化

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

        稳健而干净的方法
        • 首先,不要占用可用内存!将文件分块散列,而不是填充整个文件
        • 针对不同需求/目的的不同方法(以下所有方法或选择适用的方法):
          • 仅哈希目录树中所有条目的条目名称
          • 散列所有条目的文件内容(保留meta-like、inode-number、ctime、atime、mtime、size等,你就知道了)
          • 对于符号链接,其内容是引用名称。散列或选择跳过
          • 对条目内容进行哈希运算时,是否跟随(解析名称)符号链接
          • 如果它是一个目录,它的内容就是目录条目。当递归遍历时,它们最终将被散列,但是该级别的目录条目名称是否应该被散列以标记该目录?在需要散列以快速识别更改而不必深入遍历以散列内容的用例中非常有用。例如,文件名更改,但其余内容保持不变,它们都是相当大的文件
          • 很好地处理大文件(同样,注意RAM)
          • 处理非常深的目录树(注意打开的文件描述符)
          • 处理非标准文件名
          • 如何处理套接字、管道/FIFO、块设备、字符设备等文件?你也必须把它们弄散吗
          • 在遍历时不要更新任何条目的访问时间,因为对于某些用例来说,这将是一个副作用和反作用(直观?)
        这就是我脑子里想的,任何一个花了一段时间研究这件事的人实际上都会抓住其他的陷阱和拐弯的案子

        ,非常轻的内存,这解决了大多数情况,可能有点粗糙的边缘,但已经相当有用

        dtreetrawl
        的使用和输出示例。 人性化输出的一个片段:


        我必须检查整个目录中的文件更改

        但不包括时间戳、目录所有权

        目标是在文件相同的情况下,在任何地方获得相同的总和

        包括托管到其他计算机,而不考虑文件或对其进行更改

        md5sum * | md5sum | cut -d' ' -f1
        
        它按文件生成一个散列列表,然后将这些散列连接成一个列表

        这比tar方法快得多

        为了在散列中获得更好的隐私,我们可以在同一配方上使用sha512sum

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

        使用sha512sum时,散列值在任何地方都是相同的,但是没有已知的方法来反转散列值。

        这里是Python 3中的一个简单、简短的变体,适用于小文件
        ls -alR --full-time /folder/of/stuff | sha1sum
        
        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
        
        md5sum * | md5sum | cut -d' ' -f1
        
        sha512sum * | sha512sum | cut -d' ' -f1
        
        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()                          
        
        git ls-files <your_directory> | xargs sha256sum | cut -d" " -f1 | sha256sum | cut -d" " -f1
        
        tar -C <root-dir> -cf - --sort=name <dir> | sha256sum
        
        100%|██████████████████████████████████| 31378/31378 [03:03<00:00, 171.43file/s]