Script Bash détectant les changements dans les fichiers d'un répertoire

10

J'essaie de créer un script qui détecte si l'un des fichiers d'un répertoire a été modifié dans un intervalle de 2 secondes. Ce que j'ai jusqu'à présent, c'est:

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

Cela ne produit qu'une seule fois la valeur "Identique", je veux qu'il vérifie chaque fichier et produise "Identique" ou "Différent" pour chaque fichier.

Edit : Cela peut-il être fait sans installer le inotify-toolspackage?

dasj19
la source

Réponses:

11

Comme d'autres l'ont expliqué, l'utilisation inotifyest la meilleure solution. Je vais juste expliquer pourquoi votre script échoue. Tout d'abord, quel que soit le langage dans lequel vous programmez, chaque fois que vous essayez de déboguer quelque chose, la première règle est "imprimer toutes les variables":

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

Ainsi, comme vous pouvez le voir ci-dessus, $FILEest en fait étendu à $PWD/*. Par conséquent, la boucle n'est exécutée qu'une seule fois sur la chaîne /home/terdon/foo/* et non sur chacun des fichiers du répertoire individuellement. Ensuite, la md5sumcommande devient:

md5sum /home/terdon/foo/*

En d'autres termes, il s'exécute simultanément md5sumsur tous les fichiers du répertoire cible et non sur chacun d'eux.

Le problème est que vous citez votre expansion globale et cela l'empêche d'être développée:

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

Alors que les variables doivent presque toujours être citées , les globes ne devraient pas, car cela les transforme en chaînes au lieu de globes.

Ce que vous vouliez faire, c'est:

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

Cependant, il n'y a aucune raison de l'utiliser $PWDici, cela n'ajoute rien d'utile. La ligne ci-dessus équivaut à:

for FILE in *; do

Évitez également d'utiliser des lettres CAPITAL pour les variables shell. Ceux-ci sont utilisés pour les variables d'environnement définies par le système et il est préférable de conserver vos propres variables en minuscules.

Avec tout cela à l'esprit, voici une version améliorée et fonctionnelle de votre 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
la source
Bien qu'il for FILE in "${PWD}"/*; dofonctionne sur le même ensemble, for FILE in *; doil n'est pas exactement équivalent car ce dernier n'inclut pas les chemins d'accès.
Lambert
1
@Lambert true, mais cela ne fait aucune différence puisque, par définition, le script sera exécuté à partir de $ PWD
terdon
Ce serait une bonne idée d'utiliser md5sum -- "$file"au lieu de md5sum "$file"gérer le cas où un fichier commence par un -. Bien sûr, vous devez également faire en sorte que votre implémentation de md5sum prenne en charge le --délimiteur de fin d'options.
Harold Fischer
9

Vous pouvez certainement utiliser inotify-tools à partir de la ligne de commande, par exemple comme ceci:

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

De l' homme inotifywait

-m, --monitor

Au lieu de quitter après avoir reçu un seul événement, exécutez indéfiniment. Le comportement par défaut consiste à quitter après le premier événement.

Et voici un script qui surveille en continu, copié du fichier 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
la source
5

Vous pouvez utiliser le inotify-toolspackage pour surveiller toutes les modifications dans un dossier en temps réel. Par exemple, il contient l' inotifywaitoutil que vous pouvez utiliser comme:

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

Vous pouvez utiliser des indicateurs pour filtrer certains événements uniquement ou certains fichiers. L' inotifywatchoutil collecte les statistiques d'utilisation du système de fichiers et génère le nombre de chaque inotifyévénement.

Vous pouvez trouver plus d'exemples ici par exemple.

Si vous souhaitez surveiller avec d'autres outils, vous pouvez utiliser findavec le -mminparamètre (minutes modifiées). Puisque 2 secondes est comme 0,033 minutes, vous pouvez utiliser:

find . -type f -mmin 0.033
labyrinthes
la source
1

Si vous souhaitez surveiller sur un intervalle de deux secondes, vous pouvez entourer votre chèque avec:

while true
do
    <your steps>
    sleep 2
done

Bien que cela teste séquentiellement les fichiers et attende 2 secondes pour chaque fichier trouvé, je suggère de transformer votre chèque en fonction:

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

Qui peut être utilisé dans la whileboucle:

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

Veuillez noter l' &esperluette pour effectuer les vérifications en arrière-plan pour effectuer les vérifications de fichiers en parallèle. Notez que cela peut avoir un impact sur les performances en fonction du nombre de fichiers trouvés dans le répertoire.

Notez également que j'ai modifié les echolignes pour inclure le nom de fichier ( "$@") afin de visualiser le fichier trouvé identique / différent.

Lambert
la source
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
la source