Bash-скрипт, обнаруживающий изменения в файлах из каталога


10

Я пытаюсь создать скрипт, который определяет, были ли какие-либо файлы в каталоге изменены в течение 2 секунд. То, что я до сих пор это:

#!/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

Это выводит только один раз значение «Identical», я хочу, чтобы он проверял каждый файл и выводил «Identical» или «Different» для каждого файла.

Изменить : это можно сделать без установки inotify-toolsпакета?

Ответы:


11

Как объяснили другие, использование inotifyявляется лучшим решением. Я просто объясню, почему ваш скрипт не работает. Прежде всего, независимо от того, на каком языке вы программируете, всякий раз, когда вы пытаетесь что-то отладить, первое правило - «напечатать все переменные»:

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

Таким образом, как вы можете видеть выше, $FILEфактически расширяется до $PWD/*. Поэтому цикл запускается только один раз для строки, /home/terdon/foo/* а не для каждого файла в каталоге по отдельности. Затем md5sumкоманда становится:

md5sum /home/terdon/foo/*

Другими словами, он запускается сразу md5sumдля всех файлов в целевом каталоге, а не для каждого из них.

Проблема в том, что вы цитируете свое расширение glob, и это мешает его расширению:

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

Хотя переменные почти всегда должны заключаться в кавычки , глобусы не должны, поскольку это превращает их в строки, а не в глобусы.

То, что вы хотели сделать, это:

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

Тем не менее, здесь нет причин для использования $PWD, это не добавляет ничего полезного. Строка выше эквивалентна:

for FILE in *; do

Кроме того, избегайте использования ЗАГЛАВНЫХ букв для переменных оболочки. Они используются для системных переменных среды, и лучше хранить свои переменные в нижнем регистре.

Учитывая все это, вот рабочая, улучшенная версия вашего скрипта:

#!/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

Хотя он for FILE in "${PWD}"/*; doработает с тем же набором, поскольку for FILE in *; doон не совсем эквивалентен, поскольку последний не содержит путей.
Ламберт

1
@ Ламберт верно, но здесь нет никакой разницы, поскольку по определению сценарий будет запускаться с $ PWD
terdon

Было бы неплохо использовать md5sum -- "$file"вместо md5sum "$file"обработки случай, когда файл начинается с -. Конечно, вы также должны сделать так, чтобы ваша реализация md5sum поддерживала --конец разделителя опций.
Гарольд Фишер

9

Вы можете использовать inotify-tools определенно из командной строки, например, так:

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

От человека невинного

-m, --monitor

Вместо выхода после получения одного события, выполняйте бесконечно. Поведение по умолчанию - выход после первого события.

А вот скрипт, который непрерывно отслеживает, скопированный из файла man 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

Вы можете использовать inotify-toolsпакет для мониторинга всех изменений в папке в режиме реального времени. Например, он содержит inotifywaitинструмент, который вы можете использовать как:

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

Вы можете использовать флаги для фильтрации только определенных событий или определенных файлов. В inotifywatchсобирает инструмент файловой системы статистики и выводит отсчеты каждого inotifyсобытия.

Вы можете найти больше примеров здесь, например.

Если вы хотите отслеживать с другими инструментами, вы можете использовать findс -mminпараметром (измененные минуты). Поскольку 2 секунды равны 0,033 минутам, вы можете использовать:

find . -type f -mmin 0.033

1

Если вы хотите контролировать с интервалом в две секунды, вы можете окружить ваш чек:

while true
do
    <your steps>
    sleep 2
done

Хотя это будет последовательно проверять файлы и будет ждать 2 секунды для каждого найденного файла, я предлагаю преобразовать ваш чек в функцию:

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

Который можно использовать в whileцикле:

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

Обратите внимание на &амперсанд для выполнения проверок в фоновом режиме для параллельного выполнения проверок файлов. Обратите внимание, что это может повлиять на производительность в зависимости от количества файлов, найденных в каталоге.

Также обратите внимание, что я изменил echoстроки, чтобы включить имя файла ( "$@"), чтобы визуализировать, какой файл найден идентичным / другим.


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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.