- Kerlinux.org - http://kerlinux.org -

Utilisation de inotifywait dans des scripts Shell

Posted By SLiX On 5 août 2010 @ 15 h 05 min In Informatique | 12 Comments

Inotify (intégré au noyau Linux depuis la version 2.6.13 de Juin 2005) est un mécanisme permettant d’être immédiatement averti lors d’événements (création, lecture, écriture, …) sur des fichiers ou répertoires.

Plusieurs programmes exploitent cette fonctionnalité, dont « inotifywait » qui permet facilement de l’intégrer à des scripts Shell. Voici quelques exemples d’utilisation:

Installation

  • Sous Debian GNU/Linux:
# apt-get install inotify-tools

Attention la version 3.12 présente dans Debian Lenny a un bug avec l’affichage des dates/heures.
voir http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=458132 [1]

  • Sous RedHat Linux:
# yum install inotify-tools

1ère version

Prenons l’exemple simple de vouloir surveiller les fichiers créés dans /tmp:

#!/bin/sh

# CONFIGURATION
DIR="/tmp"
EVENTS="create"

# MAIN
inotifywait -m -e $EVENTS --timefmt '%Y-%m-%d %H:%M:%S' --format '%T %f' $DIR |
while read date time file
do
    echo "$date $time Fichier créé: $file"
done

Cela affichera:

$ ./inotify1.sh
Setting up watches.
Watches established.
2010-08-05 11:15:01 Fichier créé: rtm.flock
2010-08-05 11:15:10 Fichier créé: #sql_1354_0.MYI
2010-08-05 11:15:10 Fichier créé: #sql_1354_0.MYD
(...)

Cette façon de faire est simple mais a quelques inconvénients, que l’on constate avec les commandes suivantes:

$ nohup ./inotify1.sh > /tmp/inotify.log 2>&1
[1] 22350

$ ps f|grep inotify|grep -v grep
22350 pts/2    S      0:00  _ /bin/sh ./inotify1.sh
22351 pts/2    S      0:00  |   _ inotifywait -m -e create --timefmt %Y-%m-%d %H:%M:%S --format %T %f /tmp
22352 pts/2    S      0:00  |   _ /bin/sh ./inotify1.sh

$ kill 22350
[1]+  Complété              nohup ./inotify1.sh > /tmp/inotify.log 2>&1

$ ps f|grep inotify|grep -v grep
22352 pts/2    S      0:00 /bin/sh ./inotify1.sh
22351 pts/2    S      0:00 inotifywait -m -e create --timefmt %Y-%m-%d %H:%M:%S --format %T %f /tmp

En effet:

  • la syntaxe inotifywait (…) | while read créé un sous-processus shell
  • ce sous-processus, ainsi que la commande inotifywait ne sont pas tués si l’on tue le script shell

Amélioration avec un tube nommé

Afin d’éviter les inconvénient de la première version, il peut être pratique d’utiliser un tube nommé (named pipe ou encore FIFO) pour que le shell en cours reçoive les événements inotify:

Exemple:

#!/bin/sh

# CONFIGURATION
DIR="/tmp"
EVENTS="create"
FIFO="/tmp/inotify2.fifo"

# FUNCTIONS
on_exit() {
    kill $INOTIFY_PID
    rm $FIFO
    exit
}

# MAIN
if [ ! -e "$FIFO" ]
then
    mkfifo "$FIFO"
fi

inotifywait -m -e "$EVENTS" --timefmt '%Y-%m-%d %H:%M:%S' --format '%T %f' "$DIR" > "$FIFO" &
INOTIFY_PID=$!

trap "on_exit" 2 3 15

while read date time file
do
    echo "$date $time Fichier créé: $file"
done < "$FIFO"

on_exit

Le comportement est maintenant le suivant:

$ nohup ./inotify2.sh > /tmp/inotify.log 2>&1 &
[1] 23144

$ ps f|grep inotify|grep -v grep
23144 pts/2    S      0:00  _ /bin/sh ./inotify2.sh
23146 pts/2    S      0:00  |   _ inotifywait -m -e create --timefmt %Y-%m-%d %H:%M:%S --format %T %f /tmp

$ kill 23144
[1]+  Done                    nohup ./inotify2.sh > /tmp/inotify.log 2>&1 &

$ ps f|grep inotify|grep -v grep
$

Détail:

  • L’utilisation du tube nommé $FIFO permet de lancer la commande inotifywait en tâche de fond et de récupérer son PID
  • Il est alors possible d’installer un « trap », en l’occurrence la fonction « on_exit() », qui va tuer la commande inotifywait (et aussi effacer le tube nommé) lorsque le script est tué par un des signaux 2 (INT), 3 (QUIT) ou 15 (TERM).
  • Les événements sont lus via le tube nommé, sans création d’un shell, grâce à la fin du « while »: done < $FIFO

Pour aller un peu plus loin: opérations non bloquantes

Il reste un comportement qui peut-être gênant dans certains cas: les opérations de la boucle de lecture sont bloquantes, chaque événement devra attendre que le précédent est terminé pour être traité.

Voici une manière d’éviter cela:

#!/bin/sh

# CONFIGURATION
DIR="/tmp"
EVENTS="create"
FIFO="/tmp/inotify2.fifo"

# FUNCTIONS
on_exit() {
    kill $INOTIFY_PID
    rm $FIFO
    exit
}

on_event() {
    local date=$1
    local time=$2
    local file=$3

    sleep 5

    echo "$date $time Fichier créé: $file"
}

# MAIN
if [ ! -e "$FIFO" ]
then
    mkfifo "$FIFO"
fi

inotifywait -m -e "$EVENTS" --timefmt '%Y-%m-%d %H:%M:%S' --format '%T %f' "$DIR" > "$FIFO" &
INOTIFY_PID=$!

trap "on_exit" 2 3 15

while read date time file
do
    on_event $date $time $file &
done < "$FIFO"

on_exit

Détail:

  • La fonction on_event est lancée en tâche de fond (un sous-processus shell est en fait créé)
  • les opérations sont maintenant exécutées en parallèle; pratique pour les traitement longs ou pouvant bénéficier de multiples processeurs

Références

http://fr.wikipedia.org/wiki/Inotify [2]
http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=458132 [1]


Article printed from Kerlinux.org: http://kerlinux.org

URL to article: http://kerlinux.org/2010/08/utilisation-de-inotifywait-dans-des-scripts-shell/

URLs in this post:

[1] http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=458132: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=458132

[2] http://fr.wikipedia.org/wiki/Inotify: http://fr.wikipedia.org/wiki/Inotify

Copyright © 2010 Kerlinux.org. Tous droits réservés.