Tập lệnh Bash phát hiện sự thay đổi trong các tập tin từ một thư mục


10

Tôi đang cố gắng tạo một tập lệnh phát hiện nếu bất kỳ tệp nào trong thư mục bị thay đổi trong khoảng thời gian 2 giây. Những gì tôi có cho đến nay là:

#!/bin/bash
for FILE in "${PWD}/*"
do
    SUM1="$(md5sum $FILE)"
    sleep 2
    SUM2="$(md5sum $FILE)"
    if [ "$SUM1" = "$SUM2" ];
    then
        echo "Identical"
    else
        echo "Different"
    fi
done

Điều này chỉ xuất ra một lần Giá trị "giống hệt nhau", tôi muốn nó kiểm tra từng tệp và xuất ra "Nhận dạng" hoặc "Khác nhau" cho mỗi tệp.

Chỉnh sửa : Điều này có thể được thực hiện mà không cần cài đặt inotify-toolsgói?

Câu trả lời:


11

Như những người khác đã giải thích, sử dụng inotifylà giải pháp tốt hơn. Tôi sẽ chỉ giải thích tại sao kịch bản của bạn thất bại. Trước hết, cho dù bạn đang lập trình bằng ngôn ngữ nào, bất cứ khi nào bạn cố gắng gỡ lỗi một cái gì đó, quy tắc đầu tiên là "in tất cả các biến":

$ ls
file1  file2  file3
$ echo $PWD    
/home/terdon/foo
$ for FILE in "${PWD}/*"; do echo "$FILE"; done
/home/terdon/foo/*

Vì vậy, như bạn có thể thấy ở trên, $FILEthực sự được mở rộng đến $PWD/*. Do đó, vòng lặp chỉ được chạy một lần trên chuỗi /home/terdon/foo/* chứ không phải trên từng tệp trong thư mục riêng lẻ. Sau đó, md5sumlệnh trở thành:

md5sum /home/terdon/foo/*

Nói cách khác, nó chạy md5sumtrên tất cả các tệp trong thư mục đích cùng một lúc chứ không phải trên mỗi tệp.

Vấn đề là bạn đang trích dẫn việc mở rộng toàn cầu của mình và điều đó ngăn không cho nó được mở rộng:

$ echo "*"
*
$ echo *
file1 file2 file3

Mặc dù các biến số hầu như luôn luôn được trích dẫn , nhưng các khối không nên vì điều đó làm cho chúng thành các chuỗi thay vì các chuỗi.

Những gì bạn muốn làm là:

for FILE in "${PWD}"/*; do ...

Tuy nhiên, không có lý do để sử dụng $PWDở đây, nó không thêm bất cứ điều gì hữu ích. Dòng trên tương đương với:

for FILE in *; do

Ngoài ra, tránh sử dụng các chữ cái VỐN cho các biến shell. Chúng được sử dụng cho các biến môi trường do hệ thống thiết lập và tốt hơn là giữ các biến của riêng bạn trong trường hợp thấp hơn.

Với tất cả những điều này, đây là phiên bản cải tiến của tập lệnh của bạn:

#!/bin/bash
for file in *
do
    sum1="$(md5sum "$file")"
    sleep 2
    sum2="$(md5sum "$file")"
    if [ "$sum1" = "$sum2" ];
    then
        echo "Identical"
    else
        echo "Different"
    fi
done

Mặc dù for FILE in "${PWD}"/*; dohoạt động trên cùng một tập hợp vì for FILE in *; donó không chính xác tương đương vì cái sau không bao gồm tên đường dẫn.
Lambert

1
@Lambert đúng, nhưng nó không có gì khác biệt ở đây vì theo định nghĩa, tập lệnh sẽ được chạy từ $ PWD
terdon

Nó sẽ là một ý tưởng tốt để sử dụng md5sum -- "$file"thay vì md5sum "$file"xử lý trường hợp một tập tin bắt đầu bằng một -. Tất nhiên, bạn cũng nên làm cho việc triển khai md5sum của mình hỗ trợ --kết thúc các dấu phân cách tùy chọn.
Harold Fischer

9

Bạn có thể sử dụng inotify-tools chắc chắn từ dòng lệnh, ví dụ như thế này:

inotifywait -r  -m /dir/to/monitor/

Từ người đàn ông inotifywait

-m, --monitor

Thay vì thoát ra sau khi nhận được một sự kiện duy nhất, hãy thực hiện vô thời hạn. Hành vi mặc định là thoát sau khi sự kiện đầu tiên xảy ra.

Và đây là một đoạn script theo dõi liên tục, được sao chép từ tệp man của inotifywait:

#!/bin/sh
while inotifywait -e modify /var/log/messages; do
  if tail -n1 /var/log/messages | grep apache; then
    kdialog --msgbox "Blah blah Apache"
  fi
done

5

Bạn có thể sử dụng inotify-toolsgói để theo dõi tất cả các thay đổi trong một thư mục trong thời gian thực. Ví dụ: nó chứa inotifywaitcông cụ mà bạn có thể sử dụng như:

> inotifywait /tmp
Setting up watches.
Watches established.
/tmp/ MODIFY test

Bạn có thể sử dụng cờ để chỉ lọc các sự kiện nhất định hoặc các tệp nhất định. Công inotifywatchcụ thu thập số liệu thống kê sử dụng hệ thống tập tin và số lượng đầu ra của mỗi inotifysự kiện.

Bạn có thể tìm thấy nhiều ví dụ ở đây chẳng hạn.

Nếu bạn muốn theo dõi bằng các công cụ khác, bạn có thể sử dụng findvới -mmintham số (phút sửa đổi). Vì 2 giây giống như 0,033 phút, bạn có thể sử dụng:

find . -type f -mmin 0.033

1

Nếu bạn muốn theo dõi trong khoảng thời gian hai giây, bạn có thể bao quanh kiểm tra của mình bằng:

while true
do
    <your steps>
    sleep 2
done

Mặc dù điều này sẽ kiểm tra tuần tự các tệp và sẽ đợi 2 giây cho mỗi tệp được tìm thấy, tôi đề nghị chuyển đổi kiểm tra của bạn thành một hàm:

function _check_file()
{
    SUM1=$(md5sum "$@")
    sleep 2
    SUM2=$(md5sum "$@")
    if [ "$SUM1" == "$SUM2" ];
    then
        echo "$@: Identical"
    else
        echo "$@: Different"
    fi
}

Mà có thể được sử dụng trong whilevòng lặp:

while true
do
    for FILE in "${PWD}/"*
    do
        if [ -f "$FILE" ]
        then
            _check_file "$FILE" &
        fi
    done
    sleep 2
done

Vui lòng lưu ý ký &hiệu và thực hiện kiểm tra trong nền để thực hiện kiểm tra tệp song song. Lưu ý rằng điều này có thể ảnh hưởng đến hiệu suất tùy thuộc vào số lượng tệp được tìm thấy trong thư mục.

Cũng lưu ý rằng tôi đã thay đổi các echodòng để bao gồm tên tệp ( "$@") để trực quan hóa tệp nào được tìm thấy giống hệt / khác nhau.


0
#!/bin/bash
# pass one or more folders as arguments
while true; do
  for f in "$@"; do
    date
    echo "Checking $f and subfolders"
    find=$(find "$f" -type f)
    while read -r f2; do
      # strip non-alphanumeric from filename for a variable var name
      v=${f2//[^[:alpha:]]/}
      r=$(md5sum "$f2")
      if [ "$r" = "${!v}" ]; then
        echo "Identical $f2"
      else
        echo "Different $f2"
      fi
      eval "${v}=\$r"
    done <<< "$find"
  done
  sleep 2
done
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.