Incron, Watcher Python PyInotify Alternative. Recursively Watch A File System for Change Events using inotifywait, inotify

If you have a folder in your home directory called “/root/FilestoWatch” and in there you have a bunch of files and folders that you want to constantly be checked for changes.

There are a few options out there for this type of thing:

– crond (limited to every minute)
– incrond (cannot recursively watch a directory)
inotify (what we’re using!)
– PyInotify (python package that does what inotify does).

I had some success using Pyinotify but this requires python packages be installed which do not come standard on Red Hat Enterprise Linux (my distro of choice).

I decided to forgo that method and just use the kernel’s inotify functionality to achieve our goal.  Specifically, we’ll be using inotifywait, which is a command line utility that makes use of the inotify kernel interface.

Here’s the description from the inotifywait man page, which explains the role of the program well:

inotifywait efficiently waits for changes to files using Linux’s inotify(7) interface. It is suit-
able for waiting for changes to files from shell scripts. It can either exit once an event occurs,
or continually execute and output events as they occur.

GOOD, that’s what we want.  Luckily, inotifywait allows for a bunch of different options like being able to daemonize and spit out to a log file.  I personally prefer to run the command with the option to wait for changes over and over, that way we can deal with all the output in a shell script, which is always more fun!

So first you’ll want to install inotifywait from the EPEL repository:


vmhacks.com ~]# yum install inotify-tools

And then here’s a sample script that shows we’re checking the folder and what we’re doing if a change is found.  READ THE COMMENTS FOR DETAILED INFO!


#!/bin/bash
# inotifywait script by vmhacks.com

#so this is the main stiff, super simple one line
# -e are the events were checking for, -mrq means monitor, recursive, quiet
# --format says that we want it to just return directory (w) and filename (f)
# /root/FilestoWatch is the folder we’re watching
# –excludei means we’re ignoring case and skipping any files that match the regex string
# we then pipe any output to a while loop

/usr/bin/inotifywait -e create,delete,modify,move -mrq --format %w%f /root/FilestoWatch --excludei sess_* | while read INPUT;
do

# Get the directory name
DIRECTORY=`/usr/bin/dirname $INPUT`

# Get the filename
FILENAME=`/bin/basename $INPUT`

# Replace spaces in file names in case you want to send output to HTTP
HTTPFINAL=`/bin/echo $INPUT | /bin/sed -e 's/ /%20/g'`

# Get the current date

DATE=`date`

# Do stuff with this info
echo "ALARM! THE FILE $DIRECTORY/$FILENAME WAS CHANGED AT $DATE"
echo " "
echo "Pass this info to be processed by PHP file through HTTP"
echo "running curl http://localhost/files.php?file=$HTTPFINAL"
/usr/bin/curl "http://localhost/files.php?file=$HTTPFINAL"

done

Mm.,

2 Comments

Bart (February 20, 2014)

Hi,

i like your script, i used python watcher before but i had to install dependencies like python-inotify, python-argparse etc.

So is it possible to watch for example 2,3 or 4 directories with different reaction for each directory?

I know i could add a second directory to this Line
/usr/bin/inotifywait -e create,delete,modify,move -mrq /root/Test /root/Test2 | while read INPUT;
but the reaction would be the same.

thx

maxmouse (February 20, 2014)

Hi there thanks for reading! I’ve updated the script to provide a better way of getting the filename information as well just FYI. Probably the easiest way to achieve what you want would be to wrap the entire thing in a bash function and then run the processes in the background for each “Watched” directory. You would have to make sure you’re outputting to some files or have another way of testing it as it would now just spin off the bash processes and end the script successfully. Here’s an example:

filewatcher() {

/usr/bin/inotifywait -e create,delete,modify,move -mrq –format %w%f $1 –excludei sess_* | while read INPUT;
do

# Get the directory name
DIRECTORY=`/usr/bin/dirname “$INPUT”`

# Get the filename
FILENAME=`/bin/basename “$INPUT”`

# Get the current date
DATE=`date`

# Do stuff with this info
echo “ALARM! THE FILE $DIRECTORY/$FILENAME WAS CHANGED AT $DATE” >> /root/logfile

done
}

# this kicks off the tasks in the background
filewatcher /root/directory01 &
filewatcher /root/directory02 &

Thanks, Again!
Mm.,

Comments are closed.