Use inotify-tools on CentOS 7 or RHEL 7 to watch files and directories for events

Today, I discovered that the package inotify-tools is nowhere to be found in standard CentOS7 or RHEL7 repositories. Alternatives like incron seem to be absent as well. The inotify-tools can be used to watch a directory or file for activity and take an action when a file is changed, added, edited or simply read. You can find a workaround (or call it solution) for the absence of the inotify-tools in the repositories here.

Sometimes, for various reasons, a directory needs to be watched for new files, file changes, etc. When a change occurs, a certain action should be taken, like copying the files elsewhere or send an email with to notify someone of the changes. There are a lot of such imaginable scenarios to think of.

inotify

The inotify mechanism which exists in the Linux kernel since release 2.6.13 is ideal for purposes as described above. Inotify notifies the kernel on file changes so it’s not needed to explicitly scan the file system for changes on a regular basis. To use inotify, there are various packages available for different programming languages (like Perl or Python).

inotify-tools

The package inotify-tools contains some tools, written in C, to easily access the inotify-mechanism via the command line or bash. More information about the project can be found here.

Compiling inotify-tools for el7

Apparently, inotify-tools isn’t available (yet) for the latest releases of CentOS and RHEL in any of the popular repositories. In order to use the tools, you’ll have to compile them yourself. One big advantage is that inotify-tools doesn’t have any dependencies besides the kernel with inotify-support, so this shouldn’t be such a big problem.

To start, we need to make sure that we have the necessary tools to compile the sources so we’ll install gcc, cpp, make and their dependencies:

[jensd@cen ~]$ sudo yum install gcc make
...
=====================================================================================================
 Package              Arch         Version                  Repository                          Size
=====================================================================================================
Installing:
 gcc                  x86_64       4.8.2-16.2.el7_0         test_centos-7-x86_64-updates        16 M
Installing for dependencies:
 cpp                  x86_64       4.8.2-16.2.el7_0         test_centos-7-x86_64-updates       5.9 M
 glibc-devel          x86_64       2.17-55.el7_0.1          test_centos-7-x86_64-updates       1.0 M
 glibc-headers        x86_64       2.17-55.el7_0.1          test_centos-7-x86_64-updates       650 k
 kernel-headers       x86_64       3.10.0-123.6.3.el7       test_centos-7-x86_64-updates       1.4 M
 libmpc               x86_64       1.0.1-3.el7              test_centos-7-x86_64-base           51 k
 mpfr                 x86_64       3.1.1-4.el7              test_centos-7-x86_64-base          203 k

Transaction Summary
=====================================================================================================
Install  1 Package (+6 Dependent packages)

Total download size: 25 M
Installed size: 59 M
Is this ok [y/d/N]: y
...
Complete!

When we’re ready to build the sources, we can download them from Github here
I also created a mirror on http://jensd.be/download/inotify-tools-3.14.tar.gz

[jensd@cen ~]$ curl -O http://jensd.be/download/inotify-tools-3.14.tar.gz
 % Total % Received % Xferd Average Speed Time Time Time Current
 Dload Upload Total Spent Left Speed
100 350k 100 350k 0 0 796k 0 --:--:-- --:--:-- --:--:-- 799k

After the download, unpack the tar-archive and go into the newly created directory which contains the extracted files:

[jensd@cen ~]$ tar -xvzf inotify-tools-3.14.tar.gz
[jensd@cen ~]$ cd inotify-tools-3.14
[jensd@cen inotify-tools-3.14]$

From here, generate the Makefile by running ./configure:

[jensd@cen inotify-tools-3.14]$ ./configure
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /usr/bin/mkdir -p
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking whether make sets $(MAKE)... (cached) yes
checking for gcc... gcc
checking for C compiler default output file name... a.out
checking whether the C compiler works... yes
checking whether we are cross compiling... no
checking for suffix of executables...
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ISO C89... none needed
checking for style of include used by make... GNU
checking dependency style of gcc... gcc3
checking build system type... x86_64-unknown-linux-gnu
checking host system type... x86_64-unknown-linux-gnu
checking for a sed that does not truncate output... /usr/bin/sed
checking for grep that handles long lines and -e... /usr/bin/grep
checking for egrep... /usr/bin/grep -E
checking for fgrep... /usr/bin/grep -F
checking for ld used by gcc... /usr/bin/ld
checking if the linker (/usr/bin/ld) is GNU ld... yes
checking for BSD- or MS-compatible name lister (nm)... /usr/bin/nm -B
checking the name lister (/usr/bin/nm -B) interface... BSD nm
checking whether ln -s works... yes
checking the maximum length of command line arguments... 1572864
checking whether the shell understands some XSI constructs... yes
checking whether the shell understands "+="... yes
checking for /usr/bin/ld option to reload object files... -r
checking for objdump... objdump
checking how to recognize dependent libraries... pass_all
checking for ar... ar
checking for strip... strip
checking for ranlib... ranlib
checking command to parse /usr/bin/nm -B output from gcc object... ok
checking how to run the C preprocessor... gcc -E
checking for ANSI C header files... yes
checking for sys/types.h... yes
checking for sys/stat.h... yes
checking for stdlib.h... yes
checking for string.h... yes
checking for memory.h... yes
checking for strings.h... yes
checking for inttypes.h... yes
checking for stdint.h... yes
checking for unistd.h... yes
checking for dlfcn.h... yes
checking for objdir... .libs
checking if gcc supports -fno-rtti -fno-exceptions... no
checking for gcc option to produce PIC... -fPIC -DPIC
checking if gcc PIC flag -fPIC -DPIC works... yes
checking if gcc static flag -static works... no
checking if gcc supports -c -o file.o... yes
checking if gcc supports -c -o file.o... (cached) yes
checking whether the gcc linker (/usr/bin/ld -m elf_x86_64) supports shared libraries... yes
checking whether -lc should be explicitly linked in... no
checking dynamic linker characteristics... GNU/Linux ld.so
checking how to hardcode library paths into programs... immediate
checking whether stripping libraries is possible... yes
checking if libtool supports shared libraries... yes
checking whether to build shared libraries... yes
checking whether to build static libraries... yes
checking for doxygen... NO_DOXYGEN
checking sys/inotify.h usability... yes
checking sys/inotify.h presence... yes
checking for sys/inotify.h... yes
checking mcheck.h usability... yes
checking mcheck.h presence... yes
checking for mcheck.h... yes
checking whether sys/inotify.h actually works... yup
checking for an ANSI C-conforming const... yes
checking for inline... inline
configure: creating ./config.status
config.status: creating Makefile
config.status: creating src/Makefile
config.status: creating man/Makefile
config.status: creating libinotifytools/Makefile
config.status: creating libinotifytools/src/Makefile
config.status: creating libinotifytools/src/inotifytools/Makefile
config.status: creating config.h
config.status: creating libinotifytools/src/inotifytools/inotify.h
config.status: executing depfiles commands
config.status: executing libtool commands

Once we have a Makefile, we can start building from the inotify-tools source code and generate the executables and libs by executing make:

[jensd@cen inotify-tools-3.14]$ make
make  all-recursive
make[1]: Entering directory `/home/jensd/inotify-tools-3.14'
Making all in libinotifytools
make[2]: Entering directory `/home/jensd/inotify-tools-3.14/libinotifytools'
make[3]: Entering directory `/home/jensd/inotify-tools-3.14'
make[3]: Leaving directory `/home/jensd/inotify-tools-3.14'
Making all in src
make[3]: Entering directory `/home/jensd/inotify-tools-3.14/libinotifytools/src'
make[4]: Entering directory `/home/jensd/inotify-tools-3.14'
make[4]: Leaving directory `/home/jensd/inotify-tools-3.14'
Making all in inotifytools
make[4]: Entering directory `/home/jensd/inotify-tools-3.14/libinotifytools/src/inotifytools'
make[5]: Entering directory `/home/jensd/inotify-tools-3.14'
make[5]: Leaving directory `/home/jensd/inotify-tools-3.14'
(CDPATH="${ZSH_VERSION+.}:" && cd ../../.. && /bin/sh /home/jensd/inotify-tools-3.14/missing --run autoheader)
/home/jensd/inotify-tools-3.14/missing: line 52: autoheader: command not found
WARNING: `autoheader' is missing on your system.  You should only need it if
         you modified `acconfig.h' or `configure.ac'.  You might want
         to install the `Autoconf' and `GNU m4' packages.  Grab them
         from any GNU archive site.
rm -f stamp-h2
touch inotify.h.in
cd ../../.. && /bin/sh ./config.status libinotifytools/src/inotifytools/inotify.h
config.status: creating libinotifytools/src/inotifytools/inotify.h
config.status: libinotifytools/src/inotifytools/inotify.h is unchanged
make  all-am
make[5]: Entering directory `/home/jensd/inotify-tools-3.14/libinotifytools/src/inotifytools'
make[6]: Entering directory `/home/jensd/inotify-tools-3.14'
make[6]: Leaving directory `/home/jensd/inotify-tools-3.14'
make[5]: Leaving directory `/home/jensd/inotify-tools-3.14/libinotifytools/src/inotifytools'
make[4]: Leaving directory `/home/jensd/inotify-tools-3.14/libinotifytools/src/inotifytools'
make[4]: Entering directory `/home/jensd/inotify-tools-3.14/libinotifytools/src'
make[5]: Entering directory `/home/jensd/inotify-tools-3.14'
make[5]: Leaving directory `/home/jensd/inotify-tools-3.14'
/bin/sh ../../libtool --tag=CC   --mode=compile gcc -DHAVE_CONFIG_H -I. -I../.. -I../../libinotifytools/src/inotifytools    -std=c99 -g -O2 -MT inotifytools.lo -MD -MP -MF .deps/inotifytools.Tpo -c -o inotifytools.lo inotifytools.c
libtool: compile:  gcc -DHAVE_CONFIG_H -I. -I../.. -I../../libinotifytools/src/inotifytools -std=c99 -g -O2 -MT inotifytools.lo -MD -MP -MF .deps/inotifytools.Tpo -c inotifytools.c  -fPIC -DPIC -o .libs/inotifytools.o
inotifytools.c: In function ‘event_compare’:
inotifytools.c:2029:19: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
  int sort_event = (int)config;
                   ^
inotifytools.c: In function ‘inotifytools_wd_sorted_by_event’:
inotifytools.c:2050:45: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
  struct rbtree *ret = rbinit(event_compare, (void*)sort_event);
                                             ^
libtool: compile:  gcc -DHAVE_CONFIG_H -I. -I../.. -I../../libinotifytools/src/inotifytools -std=c99 -g -O2 -MT inotifytools.lo -MD -MP -MF .deps/inotifytools.Tpo -c inotifytools.c -o inotifytools.o >/dev/null 2>&1
mv -f .deps/inotifytools.Tpo .deps/inotifytools.Plo
/bin/sh ../../libtool --tag=CC   --mode=compile gcc -DHAVE_CONFIG_H -I. -I../.. -I../../libinotifytools/src/inotifytools    -std=c99 -g -O2 -MT redblack.lo -MD -MP -MF .deps/redblack.Tpo -c -o redblack.lo redblack.c
libtool: compile:  gcc -DHAVE_CONFIG_H -I. -I../.. -I../../libinotifytools/src/inotifytools -std=c99 -g -O2 -MT redblack.lo -MD -MP -MF .deps/redblack.Tpo -c redblack.c  -fPIC -DPIC -o .libs/redblack.o
libtool: compile:  gcc -DHAVE_CONFIG_H -I. -I../.. -I../../libinotifytools/src/inotifytools -std=c99 -g -O2 -MT redblack.lo -MD -MP -MF .deps/redblack.Tpo -c redblack.c -o redblack.o >/dev/null 2>&1
mv -f .deps/redblack.Tpo .deps/redblack.Plo
/bin/sh ../../libtool --tag=CC   --mode=link gcc -std=c99 -g -O2 -version-info 4:1:4  -o libinotifytools.la -rpath /usr/local/lib inotifytools.lo redblack.lo
libtool: link: gcc -shared  .libs/inotifytools.o .libs/redblack.o      -Wl,-soname -Wl,libinotifytools.so.0 -o .libs/libinotifytools.so.0.4.1
libtool: link: (cd ".libs" && rm -f "libinotifytools.so.0" && ln -s "libinotifytools.so.0.4.1" "libinotifytools.so.0")
libtool: link: (cd ".libs" && rm -f "libinotifytools.so" && ln -s "libinotifytools.so.0.4.1" "libinotifytools.so")
libtool: link: ar cru .libs/libinotifytools.a  inotifytools.o redblack.o
libtool: link: ranlib .libs/libinotifytools.a
libtool: link: ( cd ".libs" && rm -f "libinotifytools.la" && ln -s "../libinotifytools.la" "libinotifytools.la" )
make[4]: Leaving directory `/home/jensd/inotify-tools-3.14/libinotifytools/src'
make[3]: Leaving directory `/home/jensd/inotify-tools-3.14/libinotifytools/src'
make[3]: Entering directory `/home/jensd/inotify-tools-3.14/libinotifytools'
make[4]: Entering directory `/home/jensd/inotify-tools-3.14'
make[4]: Leaving directory `/home/jensd/inotify-tools-3.14'
make[3]: Nothing to be done for `all-am'.
make[3]: Leaving directory `/home/jensd/inotify-tools-3.14/libinotifytools'
make[2]: Leaving directory `/home/jensd/inotify-tools-3.14/libinotifytools'
Making all in src
make[2]: Entering directory `/home/jensd/inotify-tools-3.14/src'
make[3]: Entering directory `/home/jensd/inotify-tools-3.14'
make[3]: Leaving directory `/home/jensd/inotify-tools-3.14'
gcc -DHAVE_CONFIG_H -I. -I.. -I../libinotifytools/src/inotifytools    -std=c99 -I../libinotifytools/src -L../libinotifytools/src -g -O2 -MT inotifywait.o -MD -MP -MF .deps/inotifywait.Tpo -c -o inotifywait.o inotifywait.c
mv -f .deps/inotifywait.Tpo .deps/inotifywait.Po
gcc -DHAVE_CONFIG_H -I. -I.. -I../libinotifytools/src/inotifytools    -std=c99 -I../libinotifytools/src -L../libinotifytools/src -g -O2 -MT common.o -MD -MP -MF .deps/common.Tpo -c -o common.o common.c
mv -f .deps/common.Tpo .deps/common.Po
/bin/sh ../libtool --tag=CC   --mode=link gcc -std=c99 -I../libinotifytools/src -L../libinotifytools/src -g -O2   -o inotifywait inotifywait.o common.o ../libinotifytools/src/libinotifytools.la
libtool: link: gcc -std=c99 -I../libinotifytools/src -g -O2 -o .libs/inotifywait inotifywait.o common.o  -L/home/jensd/inotify-tools-3.14/libinotifytools/src ../libinotifytools/src/.libs/libinotifytools.so -Wl,-rpath -Wl,/usr/local/lib
gcc -DHAVE_CONFIG_H -I. -I.. -I../libinotifytools/src/inotifytools    -std=c99 -I../libinotifytools/src -L../libinotifytools/src -g -O2 -MT inotifywatch.o -MD -MP -MF .deps/inotifywatch.Tpo -c -o inotifywatch.o inotifywatch.c
mv -f .deps/inotifywatch.Tpo .deps/inotifywatch.Po
/bin/sh ../libtool --tag=CC   --mode=link gcc -std=c99 -I../libinotifytools/src -L../libinotifytools/src -g -O2   -o inotifywatch inotifywatch.o common.o ../libinotifytools/src/libinotifytools.la
libtool: link: gcc -std=c99 -I../libinotifytools/src -g -O2 -o .libs/inotifywatch inotifywatch.o common.o  -L/home/jensd/inotify-tools-3.14/libinotifytools/src ../libinotifytools/src/.libs/libinotifytools.so -Wl,-rpath -Wl,/usr/local/lib
make[2]: Leaving directory `/home/jensd/inotify-tools-3.14/src'
Making all in man
make[2]: Entering directory `/home/jensd/inotify-tools-3.14/man'
make[3]: Entering directory `/home/jensd/inotify-tools-3.14'
make[3]: Leaving directory `/home/jensd/inotify-tools-3.14'
make[2]: Nothing to be done for `all'.
make[2]: Leaving directory `/home/jensd/inotify-tools-3.14/man'
make[2]: Entering directory `/home/jensd/inotify-tools-3.14'
cd . && /bin/sh ./config.status config.h
config.status: creating config.h
config.status: config.h is unchanged
make[2]: Leaving directory `/home/jensd/inotify-tools-3.14'
make[1]: Leaving directory `/home/jensd/inotify-tools-3.14'

Once we compiled everything and generated the man-pages, we can do the actual “installation” by copying the generated files to their correct location. This requires superuser rights, so we need to use sudo:

[jensd@cen inotify-tools-3.14]$ sudo make install
[sudo] password for jensd:
Making install in libinotifytools
make[1]: Entering directory `/home/jensd/inotify-tools-3.14/libinotifytools'
make[2]: Entering directory `/home/jensd/inotify-tools-3.14'
make[2]: Leaving directory `/home/jensd/inotify-tools-3.14'
Making install in src
make[2]: Entering directory `/home/jensd/inotify-tools-3.14/libinotifytools/src'
make[3]: Entering directory `/home/jensd/inotify-tools-3.14'
make[3]: Leaving directory `/home/jensd/inotify-tools-3.14'
Making install in inotifytools
make[3]: Entering directory `/home/jensd/inotify-tools-3.14/libinotifytools/src/inotifytools'
make[4]: Entering directory `/home/jensd/inotify-tools-3.14'
make[4]: Leaving directory `/home/jensd/inotify-tools-3.14'
make[4]: Entering directory `/home/jensd/inotify-tools-3.14/libinotifytools/src/inotifytools'
make[5]: Entering directory `/home/jensd/inotify-tools-3.14'
make[5]: Leaving directory `/home/jensd/inotify-tools-3.14'
make[4]: Nothing to be done for `install-exec-am'.
make[4]: Nothing to be done for `install-data-am'.
make[4]: Leaving directory `/home/jensd/inotify-tools-3.14/libinotifytools/src/inotifytools'
make[3]: Leaving directory `/home/jensd/inotify-tools-3.14/libinotifytools/src/inotifytools'
make[3]: Entering directory `/home/jensd/inotify-tools-3.14/libinotifytools/src'
make[4]: Entering directory `/home/jensd/inotify-tools-3.14'
make[4]: Leaving directory `/home/jensd/inotify-tools-3.14'
make[4]: Entering directory `/home/jensd/inotify-tools-3.14/libinotifytools/src'
make[5]: Entering directory `/home/jensd/inotify-tools-3.14'
make[5]: Leaving directory `/home/jensd/inotify-tools-3.14'
test -z "/usr/local/lib" || /usr/bin/mkdir -p "/usr/local/lib"
 /bin/sh ../../libtool   --mode=install /usr/bin/install -c   libinotifytools.la '/usr/local/lib'
libtool: install: /usr/bin/install -c .libs/libinotifytools.so.0.4.1 /usr/local/lib/libinotifytools.so.0.4.1
libtool: install: (cd /usr/local/lib && { ln -s -f libinotifytools.so.0.4.1 libinotifytools.so.0 || { rm -f libinotifytools.so.0 && ln -s libinotifytools.so.0.4.1 libinotifytools.so.0; }; })
libtool: install: (cd /usr/local/lib && { ln -s -f libinotifytools.so.0.4.1 libinotifytools.so || { rm -f libinotifytools.so && ln -s libinotifytools.so.0.4.1 libinotifytools.so; }; })
libtool: install: /usr/bin/install -c .libs/libinotifytools.lai /usr/local/lib/libinotifytools.la
libtool: install: /usr/bin/install -c .libs/libinotifytools.a /usr/local/lib/libinotifytools.a
libtool: install: chmod 644 /usr/local/lib/libinotifytools.a
libtool: install: ranlib /usr/local/lib/libinotifytools.a
libtool: finish: PATH="/sbin:/bin:/usr/sbin:/usr/bin:/sbin" ldconfig -n /usr/local/lib
----------------------------------------------------------------------
Libraries have been installed in:
   /usr/local/lib

If you ever happen to want to link against installed libraries
in a given directory, LIBDIR, you must either use libtool, and
specify the full pathname of the library, or use the `-LLIBDIR'
flag during linking and do at least one of the following:
   - add LIBDIR to the `LD_LIBRARY_PATH' environment variable
     during execution
   - add LIBDIR to the `LD_RUN_PATH' environment variable
     during linking
   - use the `-Wl,-rpath -Wl,LIBDIR' linker flag
   - have your system administrator add LIBDIR to `/etc/ld.so.conf'

See any operating system documentation about shared libraries for
more information, such as the ld(1) and ld.so(8) manual pages.
----------------------------------------------------------------------
test -z "/usr/local/share/doc/inotify-tools" || /usr/bin/mkdir -p "/usr/local/share/doc/inotify-tools"
test -z "/usr/local/include" || /usr/bin/mkdir -p "/usr/local/include"
/usr/bin/mkdir -p '/usr/local/include/inotifytools'
 /usr/bin/install -c -m 644  inotifytools/inotifytools.h inotifytools/inotify-nosys.h inotifytools/inotify.h '/usr/local/include/inotifytools'
make[4]: Leaving directory `/home/jensd/inotify-tools-3.14/libinotifytools/src'
make[3]: Leaving directory `/home/jensd/inotify-tools-3.14/libinotifytools/src'
make[2]: Leaving directory `/home/jensd/inotify-tools-3.14/libinotifytools/src'
make[2]: Entering directory `/home/jensd/inotify-tools-3.14/libinotifytools'
make[3]: Entering directory `/home/jensd/inotify-tools-3.14'
make[3]: Leaving directory `/home/jensd/inotify-tools-3.14'
make[3]: Entering directory `/home/jensd/inotify-tools-3.14/libinotifytools'
make[4]: Entering directory `/home/jensd/inotify-tools-3.14'
make[4]: Leaving directory `/home/jensd/inotify-tools-3.14'
make[3]: Nothing to be done for `install-exec-am'.
make[3]: Nothing to be done for `install-data-am'.
make[3]: Leaving directory `/home/jensd/inotify-tools-3.14/libinotifytools'
make[2]: Leaving directory `/home/jensd/inotify-tools-3.14/libinotifytools'
make[1]: Leaving directory `/home/jensd/inotify-tools-3.14/libinotifytools'
Making install in src
make[1]: Entering directory `/home/jensd/inotify-tools-3.14/src'
make[2]: Entering directory `/home/jensd/inotify-tools-3.14'
make[2]: Leaving directory `/home/jensd/inotify-tools-3.14'
make[2]: Entering directory `/home/jensd/inotify-tools-3.14/src'
make[3]: Entering directory `/home/jensd/inotify-tools-3.14'
make[3]: Leaving directory `/home/jensd/inotify-tools-3.14'
test -z "/usr/local/bin" || /usr/bin/mkdir -p "/usr/local/bin"
  /bin/sh ../libtool   --mode=install /usr/bin/install -c inotifywait inotifywatch '/usr/local/bin'
libtool: install: /usr/bin/install -c .libs/inotifywait /usr/local/bin/inotifywait
libtool: install: /usr/bin/install -c .libs/inotifywatch /usr/local/bin/inotifywatch
make[2]: Nothing to be done for `install-data-am'.
make[2]: Leaving directory `/home/jensd/inotify-tools-3.14/src'
make[1]: Leaving directory `/home/jensd/inotify-tools-3.14/src'
Making install in man
make[1]: Entering directory `/home/jensd/inotify-tools-3.14/man'
make[2]: Entering directory `/home/jensd/inotify-tools-3.14'
make[2]: Leaving directory `/home/jensd/inotify-tools-3.14'
make[2]: Entering directory `/home/jensd/inotify-tools-3.14/man'
make[3]: Entering directory `/home/jensd/inotify-tools-3.14'
make[3]: Leaving directory `/home/jensd/inotify-tools-3.14'
make[2]: Nothing to be done for `install-exec-am'.
test -z "/usr/local/share/man/man1" || /usr/bin/mkdir -p "/usr/local/share/man/man1"
 /usr/bin/install -c -m 644 inotifywait.1 inotifywatch.1 '/usr/local/share/man/man1'
make[2]: Leaving directory `/home/jensd/inotify-tools-3.14/man'
make[1]: Leaving directory `/home/jensd/inotify-tools-3.14/man'
make[1]: Entering directory `/home/jensd/inotify-tools-3.14'
make[2]: Entering directory `/home/jensd/inotify-tools-3.14'
make[2]: Nothing to be done for `install-exec-am'.
make[2]: Nothing to be done for `install-data-am'.
make[2]: Leaving directory `/home/jensd/inotify-tools-3.14'
make[1]: Leaving directory `/home/jensd/inotify-tools-3.14'

At this time, the inotify-tools should be installed on your machine. Since these tools do not have any external dependencies it’s very unlikely for them to get broken during package upgrades.

[jensd@cen inotify-tools-3.14]$ ls -l /usr/local/bin/inotify*
-rwxr-xr-x. 1 root root 60892 Sep 12 15:14 /usr/local/bin/inotifywait
-rwxr-xr-x. 1 root root 55183 Sep 12 15:14 /usr/local/bin/inotifywatch

Using inotify-tools to watch a directory and take action

Now that we have a working installation of the inotify-tools, we can start using them.

The easiest way to use the tools is to create a small bash-script that will monitor a directory and takes action when something is added or changed in that directory. As an example, I created this script, watch-test.sh, that logs all actions that were taken on a certain directory.

Contents of the script:

#!/bin/bash
watchdir=/home/jensd/incoming
logfile=/home/jensd/watchlog.txt
while : ; do
        inotifywait $watchdir|while read path action file; do
                ts=$(date +"%C%y%m%d%H%M%S")
                echo "$ts :: file: $file :: $action :: $path">>$logfile
        done
done
exit 0

Don’t forget to make the file executable, then execute the script in the background (by putting the & after the command) and redirect it’s output to /dev/null in order to keep our shell clean:

[jensd@cen ~]$ chmod +x watch-test.sh
[jensd@cen ~]$ mkdir incoming
[jensd@cen ~]$ ./watch-test.sh >/dev/null 2>&1 &
[1] 61553

When do some basic actions on the folder which we are watching, we should see all actions taken in the logfile:

[jensd@cen ~]$ touch test1.txt
[jensd@cen ~]$ touch test2.txt
[jensd@cen ~]$ cp test1.txt incoming/
[jensd@cen ~]$ mv test2.txt incoming/
[jensd@cen ~]$ echo "hello">incoming/test1.txt
[jensd@cen ~]$ cat incoming/test1.txt
hello

To stop the script, simply bring it back to the foreground and stop it with Ctrl + C:

[jensd@cen ~]$ fg
./watch-test.sh > /dev/null 2>&1
^C

Looking at the log, we can see that the above executed actions: copy, move, modify and open are logged perfectly to our watchlog.txt

[jensd@cen ~]$ cat watchlog.txt
20140912161636 :: file: test1.txt :: CREATE :: /home/jensd/incoming/
20140912161643 :: file: test2.txt :: MOVED_TO :: /home/jensd/incoming/
20140912161655 :: file: test1.txt :: MODIFY :: /home/jensd/incoming/
20140912161703 :: file: test1.txt :: OPEN :: /home/jensd/incoming/

The above script is just a small example what you can do with the inotify-tools. Besides logging you could take other actions like moving, changing or executing something that is related to the files that were touched in the watched directory.

13 thoughts on “Use inotify-tools on CentOS 7 or RHEL 7 to watch files and directories for events

    • In case you followed the above steps, the tools should be installed in /usr/local/lib (see the output of “make install”)

    • Hi, thanks for the update. It wasn’t there at the time of writing the article :) Good to know.

  1. Pingback: How to zimbra live_sync with inotify-tools on CentOS 7 | Improve GNU/Linux

  2. Appreciate your effort. I was looking for something like this.
    Just i need to execute a file transfer script once file will be created into folder and move them to another servers.

  3. this six year old post was incredibly useful to me today – thanks!

    writing a system to automatically print PDFs uploaded to a directory – it works like magic!

    appreciate you leaving this up!

Leave a Reply

Your email address will not be published. Required fields are marked *