Script Bash que detecta cambios en archivos de un directorio


10

Estoy tratando de hacer un script que detecte si alguno de los archivos en un directorio se cambió dentro de un intervalo de 2 segundos. Lo que tengo hasta ahora es:

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

Esto genera solo una vez el valor "idéntico", quiero que verifique cada archivo y genere "idéntico" o "diferente" para cada archivo.

Editar : ¿Se puede hacer esto sin instalar el inotify-toolspaquete?

Respuestas:


11

Como otros han explicado, usar inotifyes la mejor solución. Solo explicaré por qué falla su script. En primer lugar, no importa en qué lenguaje esté programando, cada vez que intente depurar algo, la primera regla es "imprimir todas las variables":

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

Entonces, como puede ver arriba, en $FILErealidad se expande a $PWD/*. Por lo tanto, el bucle solo se ejecuta una vez en la cadena /home/terdon/foo/* y no en cada uno de los archivos del directorio individualmente. Entonces, el md5sumcomando se convierte en:

md5sum /home/terdon/foo/*

En otras palabras, se ejecuta md5sumen todos los archivos en el directorio de destino a la vez y no en cada uno de ellos.

El problema es que estás citando la expansión global y eso impide que se expanda:

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

Si bien las variables casi siempre deberían citarse , los globos no deberían hacerlo, ya que eso los convierte en cadenas en lugar de globos.

Lo que querías hacer es:

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

Sin embargo, no hay ninguna razón para usar $PWDaquí, no está agregando nada útil. La línea de arriba es equivalente a:

for FILE in *; do

Además, evite usar letras MAYÚSCULAS para las variables de shell. Esos se usan para las variables ambientales establecidas por el sistema y es mejor mantener sus propias variables en minúsculas.

Con todo esto en mente, aquí hay una versión mejorada y funcional de su 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

Mientras for FILE in "${PWD}"/*; dofunciona en el mismo conjunto, ya for FILE in *; doque no es exactamente equivalente porque este último no incluye los nombres de ruta.
Lambert

1
@Lambert es cierto, pero no hace ninguna diferencia aquí ya que, por definición, el script se ejecutará desde $ PWD
terdon

Sería una buena idea usar en md5sum -- "$file"lugar de md5sum "$file"manejar el caso donde un archivo comienza con a -. Por supuesto, también debe hacer que su implementación de md5sum admita el --final del delimitador de opciones.
Harold Fischer

9

Puede usar inotify-tools definitivamente desde la línea de comandos, por ejemplo, así:

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

Del hombre inotifywait

-m, --monitor

En lugar de salir después de recibir un solo evento, ejecute indefinidamente. El comportamiento predeterminado es salir después de que ocurra el primer evento.

Y aquí hay un script que monitorea continuamente, copiado del archivo 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

5

Puede usar el inotify-toolspaquete para monitorear todos los cambios en una carpeta en tiempo real. Por ejemplo, contiene la inotifywaitherramienta, que puede usar como:

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

Puede usar banderas para filtrar ciertos eventos solamente o ciertos archivos. La inotifywatchherramienta recopila estadísticas de uso del sistema de archivos y genera recuentos de cada inotifyevento.

Puede encontrar más ejemplos aquí, por ejemplo.

Si desea monitorear con otras herramientas, puede usar findcon el -mminparámetro (minutos modificados). Dado que 2 segundos son como 0.033 minutos, puede usar:

find . -type f -mmin 0.033

1

Si desea monitorear en un intervalo de dos segundos, puede rodear su cheque con:

while true
do
    <your steps>
    sleep 2
done

Si bien esto probará secuencialmente los archivos y esperará 2 segundos por cada archivo encontrado, sugiero transformar su verificación en una función:

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

Que se puede usar en el whilebucle:

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

Tenga en cuenta el &ampersand para realizar las comprobaciones en segundo plano para realizar las comprobaciones de archivos en paralelo. Tenga en cuenta que esto puede afectar el rendimiento según la cantidad de archivos encontrados en el directorio.

También tenga en cuenta que cambié las echolíneas para incluir el nombre de archivo ( "$@") para visualizar qué archivo se encuentra idéntico / diferente.


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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.