Làm cách nào để kiểm tra xem hai tệp được nén bằng nhau không?


11

Tôi đang cố gắng tiết kiệm dung lượng trong khi thực hiện sao lưu "câm" bằng cách bỏ dữ liệu vào tệp văn bản. Kịch bản sao lưu của tôi được thực thi hàng ngày và trông như thế này:

  1. Tạo một thư mục được đặt tên sau ngày sao lưu.
  2. Kết xuất một số dữ liệu vào một tệp văn bản "$name".
  3. Nếu tệp hợp lệ, gzip nó : gzip "$name". Nếu không , rm "$name".

Bây giờ tôi muốn thêm một bước bổ sung để xóa tệp nếu cùng một dữ liệu cũng có sẵn vào ngày hôm trước (và tạo liên kết tượng trưng hoặc liên kết cứng).

Lúc đầu tôi nghĩ đến việc sử dụng md5sum "$name", nhưng điều này không hiệu quả vì tôi cũng lưu tên tệp và ngày tạo.

gzipmột tùy chọn để so sánh hai tập tin được nén và cho tôi biết liệu chúng có bằng nhau hay không? Nếu gzipkhông có lựa chọn như vậy, có cách nào khác để đạt được mục tiêu của tôi không?


1
Hãy thử điều này: linux.die.net/man/1/zdiff
mreithub

2
Tôi sẽ đề nghị diff <(zcat file1) <(zcat file2), nhưng gợi ý của mrethub về zdiffngoại hình tốt hơn nhiều.
Kevin

backuppc thực hiện cho bạn những gì bạn đang cố gắng đạt được bằng tay
drone.ah

@ drohne.ah backuppc có thể là một thứ quá mức nếu nó chỉ là một tệp mỗi ngày ... (Tôi đoán nó giống như một bãi chứa SQL, nơi nó rất có ý nghĩa với gzip)
mreithub 8/213

1
@mdpc Các vấn đề thuật toán trong MD5 có thể không liên quan. Có thể tạo ra các vụ va chạm, nhưng có lẽ mối quan tâm duy nhất là những sự cố xảy ra do tình cờ chứ không phải bởi kẻ tấn công. Và điều đó vẫn không thể xảy ra cho đến khi bạn có ~ 2 ^ 64 tệp. Ngay cả một cuộc tấn công tiền định có lẽ không thành vấn đề.
derobert

Câu trả lời:


7

Bạn có thể sử dụng zcmphoặc zdiffnhư mreithub gợi ý trong nhận xét của anh ấy (hoặc lệnh của Kevin, tương tự). Chúng sẽ tương đối không hiệu quả, vì chúng thực sự giải nén cả hai tệp và sau đó chuyển chúng sang cmphoặc diff. Nếu bạn chỉ muốn trả lời "chúng giống nhau", bạn muốn cmp, nó sẽ nhanh hơn nhiều.

Cách tiếp cận của bạn với md5sumhoàn toàn tốt, nhưng bạn cần dùng MD5 trước khi chạy gzip. Sau đó lưu trữ nó trong một tệp cùng với .gztệp kết quả . Sau đó, bạn có thể so sánh tệp dễ dàng, trước khi nén nó. Nếu tên giống nhau, md5sum -csẽ làm điều này cho bạn.

$ mkdir "backup1"
$ cd backup1
$ echo "test" > backup-file
$ md5sum backup-file > backup-file.md5
$ gzip -9 backup-file

Và bản sao lưu tiếp theo:

$ mkdir "backup2"
$ cd backup2
$ echo "test" > backup-file
$ md5sum -c ../backup1/backup-file.md5 
backup-file: OK

Vì vậy, nó đã không thay đổi. OTOH, đã thay đổi:

$ echo "different" > backup-file
$ md5sum -c ../backup1/backup-file.md5 
backup-file: FAILED
md5sum: WARNING: 1 computed checksum did NOT match

Nếu bạn chuyển --quietđến nó, nó sẽ chỉ cung cấp cho bạn mã thoát. 0 cho khớp, không 0 cho khác nhau.

MD5 khá nhanh, nhưng không quá rực rỡ. MD4 ( openssl md4là thứ tốt nhất bạn có được trên dòng lệnh, tôi tin) nhanh gấp khoảng hai lần (không phải MD5 cũng không an toàn, nhưng cả hai đều có khả năng chống va chạm khi không ai cố gắng lật đổ chúng). SHA-1 ( sha1sum) an toàn hơn, nhưng chậm hơn; SHA-256 ( sha256sum) an toàn, nhưng thậm chí còn chậm hơn. CRC32 nên nhanh hơn nhiều lần, nhưng ngắn hơn và do đó sẽ có nhiều va chạm ngẫu nhiên hơn. Nó cũng hoàn toàn không an toàn.


zdiffCó vẻ như một sự lãng phí vì tôi chỉ muốn biết liệu một tập tin đã thay đổi, không phải những gì . zcmpcó vẻ thú vị, tôi sẽ thử nó
Lekensteyn

7

Câu trả lời của @derobert là tuyệt vời, mặc dù tôi muốn chia sẻ một số thông tin khác mà tôi đã tìm thấy.

gzip -l -v

Các tệp được nén bằng gzip đã chứa một hàm băm (mặc dù không an toàn, hãy xem bài đăng SO này ):

$ echo something > foo
$ gzip foo
$ gzip -v -l foo.gz 
method  crc     date  time           compressed        uncompressed  ratio uncompressed_name
defla 18b1f736 Feb  8 22:34                  34                  10 -20.0% foo

Người ta có thể kết hợp CRC và kích thước không nén để có được dấu vân tay nhanh chóng:

gzip -v -l foo.gz | awk '{print $2, $7}'

cmp

Để kiểm tra xem hai byte có bằng nhau hay không, sử dụng cmp file1 file2. Bây giờ, một tệp được nén có một số tiêu đề với dữ liệu và chân trang (CRC cộng với kích thước gốc) được nối thêm. Các mô tả về các định dạng gzip cho thấy tiêu đề chứa các thời điểm khi các tập tin được nén và rằng tên tập tin là một chuỗi nul-chấm dứt mà được gắn sau header 10 byte.

Vì vậy, giả sử rằng tên tệp là hằng số và cùng một lệnh ( gzip "$name") được sử dụng, người ta có thể kiểm tra xem hai tệp có khác nhau hay không bằng cách sử dụng cmpvà bỏ qua các byte đầu tiên bao gồm cả thời gian:

cmp -i 8 file1 file2

Lưu ý : giả định rằng các tùy chọn nén giống nhau rất quan trọng, nếu không lệnh sẽ luôn báo cáo tệp là khác nhau. Điều này xảy ra vì các tùy chọn nén được lưu trữ trong tiêu đề và có thể ảnh hưởng đến dữ liệu nén. cmpchỉ nhìn vào byte thô và không hiểu nó là gzip.

Nếu bạn có tên tệp có cùng độ dài, thì bạn có thể thử tính toán các byte sẽ bị bỏ qua sau khi đọc tên tệp. Khi tên tệp có kích thước khác nhau, bạn có thể chạy cmpsau khi bỏ qua byte, như thế cmp <(cut -b9- file1) <(cut -b10- file2).

zcmp

Đây chắc chắn là cách tốt nhất để thực hiện, đầu tiên nó nén dữ liệu và bắt đầu so sánh các byte với cmp(thực sự, đây là những gì được thực hiện trong shellscript zcmp( zdiff)).

Một lưu ý, đừng sợ ghi chú sau trong trang hướng dẫn:

Khi cả hai tệp phải được giải nén trước khi so sánh, tệp thứ hai không được nén thành / tmp. Trong tất cả các trường hợp khác, zdiff và zcmp chỉ sử dụng một đường ống.

Khi bạn có Bash mới, nén sẽ không sử dụng tệp tạm thời, chỉ là một đường ống. Hoặc, như zdiffnguồn tin nói:

# Reject Solaris 8's buggy /bin/bash 2.03.

Nếu byte 4 (FLG) bằng 0 thì tên tệp không có trong tiêu đề, vì vậy bạn không cần phải lo lắng về độ dài của nó. Ngoài ra, tôi thấy gzip -v -lsẽ báo cáo thời gian tệp thay vì MTIME nếu bốn byte MTIME trong tiêu đề bằng không. Cũng lưu ý nếu MTIME ở đó, nó thường là một chút trước thời gian tập tin vì đó là khi quá trình nén bắt đầu.
kitchin

0

Để so sánh hai tệp gzip, chỉ cần nội dung, một lệnh, không diff, chỉ cần so sánhmd5sum

$ diff -q <(zcat one.gz|md5sum|cut -f1 -d' ') \
          <(zcat two.gz|md5sum|cut -f1 -d' ') \
    && echo same || echo not_same

Bạn cũng có thể "lọc" các khác biệt có liên quan,

$ diff -q <(zcat one.gz|grep -v '^-- Dump completed'|md5sum|cut -f1 -d' ') \
          <(zcat two.gz|grep -v '^-- Dump completed'|md5sum|cut -f1 -d' ') \
   && echo same || echo not_same

Nếu viết kịch bản, tôi khuyên dùng chức năng lọc (không được kiểm tra, chỉ là một ví dụ),

do_filter_sum() {
  zcat $1 | grep -v '^-- Dump completed' | md5sum | cut -f1 -d' '
}

diff -q <(do_filter_sum one.gz) \
        <(do_filter_sum two.gz) \
        && echo same || echo not_same

Md5sum là một sự lãng phí, bạn có thể sử dụng cmp. zcatgrepcó thể được sáp nhập vào zgrep.
Lekensteyn

đúng, md5sum không cần thiết phải so sánh (trừ khi bạn đã tạo chúng); Tôi chỉ sử dụng nó kể từ khi derobert sử dụng nó. zgrep chỉ là một tập lệnh về cơ bản thực hiện gunzip và grep (hoặc sed như trường hợp có thể), do đó có rất ít sự khác biệt ở đó. kịch bản như được đăng được cố ý hiển thị như một chuỗi các đường ống với các phần có thể cắm được; có gì vui khi hợp nhất mọi thứ thành một lệnh duy nhất?
michael

1
zcatchỉ là gunzip -c. Sử dụng đúng công cụ cho đúng công việc, KISS tốt hơn là phình to. Trong trường hợp này, tôi sẽ dành thời gian của mình để viết một cái gì đó tạo ra các liên kết cứng khi cần thiết, điều đó thú vị hơn.
Lekensteyn
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.