Linux: tính toán một hàm băm cho một thư mục và nội dung nhất định?


92

Chắc chắn phải có một cách để làm điều này một cách dễ dàng!

Tôi đã thử các ứng dụng dòng lệnh Linux chẳng hạn như sha1summd5sumnhưng chúng dường như chỉ có thể tính toán hàm băm của các tệp riêng lẻ và xuất ra danh sách các giá trị băm, một giá trị cho mỗi tệp.

Tôi cần tạo một hàm băm duy nhất cho toàn bộ nội dung của một thư mục (không chỉ tên tệp).

Tôi muốn làm một cái gì đó giống như

sha1sum /folder/of/stuff > singlehashvalue

Chỉnh sửa: để làm rõ, các tệp của tôi ở nhiều cấp trong một cây thư mục, chúng không nằm trong cùng một thư mục gốc.


1
Theo ý bạn là 'toàn bộ nội dung' là dữ liệu logic của tất cả các tệp trong thư mục hay dữ liệu của nó cùng với meta khi đến mã băm gốc? Vì tiêu chí lựa chọn trường hợp sử dụng của bạn khá rộng, tôi đã cố gắng đề cập đến một số tiêu chí thực tế trong câu trả lời của mình.
sáu giờ

Câu trả lời:


119

Một cách khả thi sẽ là:

đường dẫn sha1sum / đến / thư mục / * | sha1sum

Nếu có toàn bộ cây thư mục, có lẽ bạn nên sử dụng find và xargs. Một lệnh khả thi sẽ là

tìm đường dẫn / đến / thư mục -type f -print0 | sắp xếp -z | xargs -0 sha1sum | sha1sum

Và, cuối cùng, nếu bạn cũng cần tính đến quyền và thư mục trống:

(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

Các đối số của statsẽ làm cho nó in tên của tệp, theo sau là các quyền của nó. Hai lần tìm sẽ chạy lần lượt, gây ra gấp đôi số lượng IO của đĩa, lần đầu tiên tìm tất cả tên tệp và kiểm tra tổng nội dung, lần thứ hai tìm tất cả tên tệp và thư mục, tên in và chế độ. Sau đó, danh sách "tên tệp và tổng kiểm tra", tiếp theo là "tên và thư mục, có quyền" sau đó sẽ được tổng kiểm tra, cho một tổng kiểm tra nhỏ hơn.


2
và đừng quên đặt LC_ALL = POSIX, để các công cụ khác nhau tạo ra đầu ra độc lập với ngôn ngữ.
David Schmitt,

2
Tôi tìm thấy con mèo | sha1sum nhanh hơn sha1sum đáng kể | sha1sum. YMMV, hãy thử từng cái này trên hệ thống của bạn: time find path / to / folder -type f -print0 | sắp xếp -z | xargs -0 sha1sum | sha1sum; tìm thời gian đường dẫn / đến / thư mục -type f -print0 | sắp xếp -z | mèo xargs -0 | sha1sum
Bruno Bronosky

5
@RichardBronosky - Giả sử chúng ta có hai tệp, A và B. A chứa "foo" và B chứa "bar was here". Với phương pháp của bạn, chúng tôi sẽ không thể tách tệp đó khỏi hai tệp C và D, trong đó C chứa "foobar" và D chứa "đã ở đây". Bằng cách băm từng tệp riêng lẻ và sau đó băm tất cả các cặp "tên tệp băm", chúng ta có thể thấy sự khác biệt.
Vatine

2
Để thực hiện công việc này không phân biệt đường dẫn thư mục (tức là khi bạn muốn so sánh các hàm băm của hai thư mục khác nhau), bạn cần sử dụng một đường dẫn tương đối và thay đổi thành thư mục thích hợp, vì các đường dẫn được bao gồm trong hàm băm cuối cùng:find ./folder -type f -print0 | sort -z | xargs -0 sha1sum | sha1sum
robbles

2
@robbles Đó là chính xác và tại sao tôi không đặt chữ đầu tiên /vào path/to/folderbit.
Vatine

25
  • Sử dụng công cụ phát hiện xâm nhập hệ thống tệp như trợ lý .

  • băm một quả bóng tar của thư mục:

    tar cvf - /path/to/folder | sha1sum

  • Tự viết mã thứ gì đó, chẳng hạn như oneliner của vatine :

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


3
+1 cho dung dịch hắc ín. Đó là nhanh nhất, nhưng giảm độ dài v. Chỉ làm chậm lại.
Bruno Bronosky

6
lưu ý rằng dung dịch tar giả định các tệp có cùng thứ tự khi bạn so sánh chúng. Liệu chúng có phụ thuộc vào hệ thống tệp mà tệp nằm trong khi thực hiện so sánh.
nos

5
Hàm băm git không phù hợp cho mục đích này vì nội dung tệp chỉ là một phần đầu vào của nó. Ngay cả đối với cam kết ban đầu của một nhánh, băm cũng bị ảnh hưởng bởi thông báo cam kết và siêu dữ liệu cam kết, như thời gian của cam kết. Nếu bạn cam kết cùng một cấu trúc thư mục nhiều lần, bạn sẽ nhận được các hàm băm khác nhau mỗi lần, do đó hàm băm kết quả không phù hợp để xác định xem hai thư mục có phải là bản sao chính xác của nhau hay không bằng cách chỉ gửi hàm băm qua.
Zoltan

1
@Zoltan băm git là hoàn toàn ổn, nếu bạn sử dụng băm cây chứ không phải băm cam kết.
hobbs

@hobbs Câu trả lời được nêu ban đầu là "commit hash", chắc chắn không phù hợp cho mục đích này. Băm cây nghe có vẻ là một ứng cử viên tốt hơn nhiều, nhưng vẫn có thể có những cái bẫy ẩn. Một điều tôi nghĩ đến là việc đặt bit thực thi trên một số tệp sẽ thay đổi băm cây. Bạn phải phát hành git config --local core.fileMode falsetrước khi cam kết để tránh điều này. Tôi không biết liệu có bất kỳ cảnh báo như thế này hay không.
Zoltan

14

Bạn có thể làm tar -c /path/to/folder | sha1sum


16
Nếu bạn muốn sao chép tổng kiểm tra đó trên một máy khác, tar có thể không phải là lựa chọn tốt, vì định dạng dường như có chỗ cho sự mơ hồ và tồn tại trong nhiều phiên bản, vì vậy tar trên một máy khác có thể tạo ra kết quả khác với các tệp giống nhau.
slowdog

2
mối quan tâm hợp lệ slowdog của dù, nếu bạn quan tâm đến nội dung tập tin, quyền, vv nhưng không thay đổi thời gian, bạn có thể thêm các --mtimetùy chọn như vậy: tar -c /path/to/folder --mtime="1970-01-01" | sha1sum.
Phile nhị phân

@ S. Lott nếu kích thước thư mục là lớn, tôi có nghĩa là nếu kích thước của thư mục là quá lớn, nén nó và nhận được md5 vào nó sẽ mất thời gian hơn
Kasun Siyambalapitiya

11

Nếu bạn chỉ muốn kiểm tra xem có gì đó trong thư mục đã thay đổi hay không, tôi khuyên bạn nên làm như sau:

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

Nó sẽ chỉ cung cấp cho bạn một hàm băm của đầu ra ls, chứa các thư mục, thư mục con, tệp của chúng, dấu thời gian, kích thước và quyền của chúng. Khá nhiều thứ mà bạn cần để xác định xem có điều gì đó đã thay đổi hay không.

Xin lưu ý rằng lệnh này sẽ không tạo hàm băm cho mỗi tệp, nhưng đó là lý do tại sao lệnh này sẽ nhanh hơn so với việc sử dụng find.


1
Tôi không chắc tại sao điều này không có nhiều ủng hộ hơn do sự đơn giản của giải pháp. Bất cứ ai có thể giải thích tại sao điều này sẽ không hoạt động tốt?
Dave C

1
Tôi cho rằng điều này không lý tưởng vì hàm băm được tạo sẽ dựa trên chủ sở hữu tệp, thiết lập định dạng ngày, v.v.
Ryota

1
Lệnh ls có thể được tùy chỉnh để xuất ra bất cứ thứ gì bạn muốn. Bạn có thể thay thế -l bằng -gG để bỏ qua nhóm và chủ sở hữu. Và bạn có thể thay đổi định dạng ngày với tùy chọn --time-style. Về cơ bản, hãy kiểm tra trang người đàn ông của ls và xem những gì phù hợp với nhu cầu của bạn.
Shumoapp

@DaveC Bởi vì nó khá vô dụng. Nếu bạn muốn so sánh tên tệp, chỉ cần so sánh chúng trực tiếp. Chúng không lớn như vậy.
Navin

5
@Navin Từ câu hỏi, không rõ liệu có cần thiết phải băm nội dung tệp hay phát hiện sự thay đổi trong cây hay không. Mỗi trường hợp có những công dụng của nó. Ví dụ, lưu trữ 45K tên tệp trong cây nhân ít thực tế hơn so với một hàm băm. ls -lAgGR --block-size = 1 --time-style = +% s | sha1sum hoạt động tuyệt vời đối với tôi
yashma

5

Một cách tiếp cận mạnh mẽ và sạch sẽ

  • Điều đầu tiên trước tiên, đừng tích trữ bộ nhớ có sẵn ! Băm một tập tin thành nhiều phần thay vì cung cấp toàn bộ tập tin.
  • Các cách tiếp cận khác nhau cho các nhu cầu / mục đích khác nhau (tất cả các cách dưới đây hoặc chọn những gì từng áp dụng):
    • Chỉ băm tên mục nhập của tất cả các mục nhập trong cây thư mục
    • Băm nội dung tệp của tất cả các mục nhập (để lại meta như, số inode, ctime, atime, mtime, size, v.v., bạn sẽ có ý tưởng)
    • Đối với một liên kết tượng trưng, ​​nội dung của nó là tên tham chiếu. Băm nó hoặc chọn bỏ qua
    • Theo dõi hoặc không theo dõi (tên đã phân giải) liên kết biểu tượng trong khi băm nội dung của mục nhập
    • Nếu đó là một thư mục, nội dung của nó chỉ là các mục nhập thư mục. Trong khi duyệt đệ quy, cuối cùng chúng sẽ được băm nhưng các tên mục nhập thư mục của cấp đó có nên được băm để gắn thẻ thư mục này không? Hữu ích trong các trường hợp sử dụng khi hàm băm được yêu cầu để xác định thay đổi một cách nhanh chóng mà không cần phải đi sâu để băm nội dung. Một ví dụ sẽ là tên tệp thay đổi nhưng phần còn lại của nội dung vẫn giữ nguyên và chúng đều là tệp khá lớn
    • Xử lý tốt các tệp lớn (một lần nữa, hãy lưu ý đến RAM)
    • Xử lý cây thư mục rất sâu (lưu ý đến các bộ mô tả tệp đang mở)
    • Xử lý tên tệp không chuẩn
    • Làm thế nào để tiếp tục với các tệp là ổ cắm, đường ống / FIFO, thiết bị khối, thiết bị char? Cũng phải băm chúng?
    • Không cập nhật thời gian truy cập của bất kỳ mục nhập nào khi đang duyệt vì đây sẽ là một tác dụng phụ và phản tác dụng (trực quan?) Đối với một số trường hợp sử dụng nhất định.

Đây là những gì tôi đặt ra trên đầu, bất kỳ ai đã dành thời gian làm việc trên thực tế này sẽ có thể nắm bắt được các vấn đề khác và các trường hợp góc.

Đây là một công cụ , rất nhẹ về bộ nhớ, giải quyết hầu hết các trường hợp, có thể hơi thô xung quanh các cạnh nhưng khá hữu ích.

Một ví dụ sử dụng và đầu ra của 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

Một đoạn mã đầu ra thân thiện với con người:

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

1
Bạn có thể đưa ra một ví dụ ngắn gọn để có được một thư mục sha256 mạnh mẽ và sạch sẽ, có thể cho một thư mục Windows với ba thư mục con và một vài tệp trong mỗi thư mục?
Ferit

3

Nếu bạn chỉ muốn băm nội dung của tệp, bỏ qua tên tệp thì bạn có thể sử dụng

cat $FILES | md5sum

Đảm bảo rằng bạn có các tệp theo cùng một thứ tự khi tính toán băm:

cat $(echo $FILES | sort) | md5sum

Nhưng bạn không thể có thư mục trong danh sách tệp của mình.


2
Di chuyển phần cuối của một tệp vào phần đầu của tệp theo thứ tự bảng chữ cái sẽ không ảnh hưởng đến hàm băm nhưng sẽ không ảnh hưởng đến hàm băm. Dấu phân tách tệp hoặc độ dài tệp cần được bao gồm trong hàm băm.
Jason Stangroome

3

Nếu đây là git repo và bạn muốn bỏ qua bất kỳ tệp nào trong đó .gitignore, bạn có thể muốn sử dụng cái này:

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

Điều này đang làm việc tốt cho tôi.


Cảm ơn rất nhiều! :)
visortelle

Đối với nhiều ứng dụng, cách tiếp cận này là ưu việt. Chỉ băm các tệp mã nguồn sẽ có được một hàm băm đủ duy nhất trong thời gian ngắn hơn rất nhiều.
John McGehee

2

Có một tập lệnh python cho điều đó:

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

Nếu bạn thay đổi tên của một tệp mà không thay đổi thứ tự bảng chữ cái của chúng, tập lệnh băm sẽ không phát hiện ra nó. Tuy nhiên, nếu bạn thay đổi thứ tự của các tệp hoặc nội dung của bất kỳ tệp nào, việc chạy tập lệnh sẽ cung cấp cho bạn một hàm băm khác với trước đây.


2

Một công cụ khác để đạt được điều này:

http://md5deep.sourceforge.net/

Như là âm thanh: giống như md5sum nhưng cũng đệ quy, cộng với các tính năng khác.


1
Mặc dù liên kết này có thể trả lời câu hỏi, nhưng tốt hơn hết bạn nên đưa các phần thiết yếu của câu trả lời vào đây và cung cấp liên kết để tham khảo. Các câu trả lời chỉ có liên kết có thể trở nên không hợp lệ nếu trang được liên kết thay đổi.
Mamoun Benghezal,

1

Hãy thử làm theo hai bước:

  1. tạo một tệp với hàm băm cho tất cả các tệp trong một thư mục
  2. băm tệp này

Như vậy:

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

Hoặc làm tất cả cùng một lúc:

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

for F in 'find ...' ...không hoạt động khi bạn có khoảng trắng trong tên (điều mà bạn luôn làm hiện nay).
mivk

1

Tôi sẽ chuyển kết quả cho các tệp riêng lẻ qua sort(để ngăn chặn việc sắp xếp lại các tệp để thay đổi hàm băm) thành md5sumhoặc sha1sum, tùy theo bạn chọn.


1

Tôi đã viết một script Groovy để thực hiện việc này:

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)}"

Bạn có thể tùy chỉnh cách sử dụng để tránh in từng tệp, thay đổi thông báo thư, lấy băm thư mục ra, v.v. Tôi đã thử nghiệm nó dựa trên dữ liệu thử nghiệm NIST và nó hoạt động như mong đợi. 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

1

Tôi đã phải kiểm tra toàn bộ thư mục để thay đổi tệp.

Nhưng với việc loại trừ, dấu thời gian, quyền sở hữu thư mục.

Mục tiêu là nhận được một tổng giống hệt nhau ở bất kỳ đâu, nếu các tệp giống hệt nhau.

Bao gồm được lưu trữ vào các máy khác, bất kể bất cứ thứ gì ngoại trừ các tệp hoặc thay đổi đối với chúng.

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

Nó tạo ra một danh sách các băm theo tệp, sau đó nối các băm đó thành một.

Đây là cách nhanh hơn so với phương pháp tar.

Để có sự riêng tư mạnh mẽ hơn trong hàm băm, chúng ta có thể sử dụng sha512sum trên cùng một công thức.

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

Các băm cũng giống nhau ở bất kỳ đâu sử dụng sha512sum nhưng không có cách nào được biết để đảo ngược nó.


Điều này có vẻ đơn giản hơn nhiều so với câu trả lời được chấp nhận cho việc băm một thư mục. Tôi không tìm thấy câu trả lời được chấp nhận đáng tin cậy. Một vấn đề ... có khả năng các băm có thể xuất hiện theo một thứ tự khác không? sha256sum /tmp/thd-agent/* | sortlà những gì tôi đang cố gắng cho một đơn đặt hàng đáng tin cậy, sau đó chỉ cần băm điều đó.
thinktt

Xin chào, có vẻ như các băm có thứ tự bảng chữ cái theo mặc định. Ý bạn là gì khi đặt hàng đáng tin cậy? Bạn phải tổ chức tất cả những điều đó một mình. Ví dụ: sử dụng mảng kết hợp, entry + hash. Sau đó, bạn sắp xếp mảng này theo mục nhập, điều này cung cấp một danh sách các hàm băm được tính toán theo thứ tự sắp xếp. Tôi tin rằng bạn có thể sử dụng một đối tượng json và băm trực tiếp toàn bộ đối tượng.
NVRM

Nếu tôi hiểu, bạn đang nói rằng nó sẽ băm các tệp theo thứ tự bảng chữ cái. Điều đó có vẻ đúng. Điều gì đó trong câu trả lời được chấp nhận ở trên đôi khi đưa ra cho tôi các đơn đặt hàng khác nhau không liên tục, vì vậy tôi chỉ đang cố gắng đảm bảo điều đó không xảy ra nữa. Tôi sẽ gắn bó với việc sắp xếp cuối cùng. Có vẻ như đang hoạt động. Chỉ có vấn đề với phương pháp này so với câu trả lời được chấp nhận mà tôi thấy là nó không đối phó với các thư mục lồng nhau. Trong trường hợp của tôi, tôi không có bất kỳ thư mục nào nên điều này hoạt động tốt.
thinktt

thì ls -r | sha256sumsao?
NVRM

@NVRM đã thử nó và nó chỉ kiểm tra các thay đổi tên tệp chứ không phải nội dung tệp
Gi0rgi0s

0

Bạn có thể sha1sumtạo danh sách các giá trị băm và sau đó lập sha1sumlại danh sách đó, điều đó phụ thuộc vào chính xác những gì bạn muốn đạt được.


0

Đây là một biến thể đơn giản, ngắn gọn trong Python 3 hoạt động tốt với các tệp có kích thước nhỏ (ví dụ: cây nguồn hoặc thứ gì đó, trong đó mọi tệp riêng lẻ có thể vừa với RAM dễ dàng), bỏ qua các thư mục trống, dựa trên ý tưởng từ các giải pháp khác:

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()                          

Nó hoạt động như thế này:

  1. Tìm tất cả các tệp trong thư mục một cách đệ quy và sắp xếp chúng theo tên
  2. Tính toán băm (mặc định: SHA-1) của mọi tệp (đọc toàn bộ tệp vào bộ nhớ)
  3. Tạo chỉ mục dạng văn bản với các dòng "filename = hash"
  4. Mã hóa chỉ mục đó trở lại thành chuỗi byte UTF-8 và băm

Bạn có thể chuyển một hàm băm khác làm tham số thứ hai nếu SHA-1 không phải là tách trà của bạn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.