Script Bash que detecta alterações nos arquivos de um diretório

10

Estou tentando criar um script que detecta se algum dos arquivos em um diretório foi alterado dentro de um intervalo de 2 segundos. O que tenho até agora é:

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

Isso gera apenas uma vez o valor "Idêntico", quero que ele verifique cada arquivo e produza "Idêntico" ou "Diferente" para cada arquivo.

Edit : Isso pode ser feito sem instalar o inotify-toolspacote?

dasj19
fonte

Respostas:

11

Como outros explicaram, usar inotifyé a melhor solução. Vou apenas explicar por que seu script falha. Primeiro de tudo, não importa em qual idioma você está programando, sempre que você tentar depurar algo, a primeira regra é "imprimir todas as variáveis":

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

Então, como você pode ver acima, $FILEé realmente expandido para $PWD/*. Portanto, o loop é executado apenas uma vez na cadeia /home/terdon/foo/* e não em cada um dos arquivos no diretório individualmente. Então, o md5sumcomando se torna:

md5sum /home/terdon/foo/*

Em outras palavras, ele é executado md5sumem todos os arquivos no diretório de destino de uma só vez e não em cada um deles.

O problema é que você está citando sua expansão glob e impede que ela seja expandida:

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

Enquanto as variáveis quase sempre devem ser citadas , os globs não devem, pois isso os transforma em strings em vez de globs.

O que você pretendia fazer é:

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

No entanto, não há razão para usar $PWDaqui, não está adicionando nada de útil. A linha acima é equivalente a:

for FILE in *; do

Além disso, evite usar letras maiúsculas para variáveis ​​de shell. Elas são usadas para as variáveis ​​ambientais definidas pelo sistema e é melhor manter suas próprias variáveis ​​em minúsculas.

Com tudo isso em mente, aqui está uma versão melhorada e funcional do seu script:

#!/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
terdon
fonte
Enquanto for FILE in "${PWD}"/*; dotrabalha no mesmo conjunto, for FILE in *; donão é exatamente equivalente, porque o último não inclui os nomes de caminho.
Lambert
1
@ Lambert true, mas não faz diferença aqui, pois, por definição, o script será executado a partir de $ PWD
terdon
Seria uma boa idéia usar em md5sum -- "$file"vez de md5sum "$file"lidar com o caso em que um arquivo começa com a -. Obviamente, você também deve fazer sua implementação do md5sum suportar o --delimitador de final de opções.
Harold Fischer
9

Você pode usar as ferramentas inotify definitivamente na linha de comando, por exemplo:

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

De man inotifywait

-m, --monitor

Em vez de sair depois de receber um único evento, execute indefinidamente. O comportamento padrão é sair após o primeiro evento.

E aqui está um script que monitora continuamente, copiado do arquivo man de 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
Rahul
fonte
5

Você pode usar o inotify-toolspacote para monitorar todas as alterações em uma pasta em tempo real. Por exemplo, ele contém a inotifywaitferramenta, que você pode usar como:

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

Você pode usar sinalizadores para filtrar apenas determinados eventos ou determinados arquivos. A inotifywatchferramenta coleta estatísticas de uso do sistema de arquivos e gera contagens de cada inotifyevento.

Você pode encontrar mais exemplos aqui, por exemplo.

Se você deseja monitorar com outras ferramentas, pode usar findcom o -mminparâmetro (minutos modificados). Como 2 segundos é de 0,033 minutos, você pode usar:

find . -type f -mmin 0.033
mazs
fonte
1

Se você deseja monitorar em um intervalo de dois segundos, pode cercar seu cheque com:

while true
do
    <your steps>
    sleep 2
done

Enquanto isso testará seqüencialmente os arquivos e aguardará 2 segundos para cada arquivo encontrado, sugiro que você transforme sua verificação em uma função:

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

Que pode ser usado no whileloop:

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

Observe &oe comercial para executar as verificações em segundo plano para executar as verificações de arquivo em paralelo. Observe que isso pode afetar o desempenho, dependendo do número de arquivos encontrados no diretório.

Observe também que alterei as echolinhas para incluir o nome do arquivo ( "$@") para visualizar qual arquivo é encontrado idêntico / diferente.

Lambert
fonte
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
dw1
fonte