Bash script to monitor file change and execute command
Solution 1:
Linux provides a nice interface for monitoring all file system events like creating, modifying, removing files. The interface is inotify
family of system calls, the userspace utilities leveraging these calls are provided by the inotify-tools
package in Ubuntu (available on the universe repository). If you don't have it already, install by:
sudo apt-get install inotify-tools
inotify-tools
provides inotifywait
and inotifywatch
binaries, we need the first one.
So you want to run the command asciidoctor -q some_file
when any .adoc
file is modified (some_file
will replaced by that), if so assuming your .adoc
files are in directory /foo/bar
, you can set the watch:
inotifywait -qm --event modify --format '%w' /foo/bar/*.adoc
-q
enables the quiet mode, no info frominotifywait
itself-m
enables monitor mode, otherwise it will exit after the first event--event modify
, we are only interested inmodify
event i.e. when a file is modified. Other possible values includeopen
,close
etc.--format %w
, we only want the file name that is modified rather than bunch of other info as we will use the file name as input to another command/foo/bar/*.adoc
will be expanded to all.adoc
files under/foo/bar
directory
Now the above will show you the filename whenever any is modified, now to run the command on the filename (assuming the command takes arguments via STDIN):
inotifywait -qm --event modify --format '%w' /foo/bar/*.adoc | asciidoctor -q
You can also setup a recursive watch on the directory, you will then need to use grep
to filter the desired files only. Here setting the watch recursively (-r
) on directory /foo/bar
and using grep
to filter only .adoc
files:
inotifywait -qrm --event modify --format '%w%f' /foo/bar | grep '\.adoc$' | asciidoctor -q
When watching directories the output format specifier %w
resolves to the directory name, so we need %f
to get the file name. While watching files, %f
would resolve to empty string.
Note that, you can also run inotifywait
in daemon (-d
) mode, you can also script the whole thing, and/or run in background, and/or play with it more other options.
Also, you can replace asciidoctor
with any other command of your choice, if you want.
Check man inotifywait
to get more idea.
Solution 2:
There are many tools out there, specially if you search through your repository
apt-cache search monitor | grep file
which gives us many tools. But you do not have to test them because I did that.
I have tested these tools and many others:
- inotify
- fswatch
- filewatch
- swatch
- fileschanged
- entr
Among these tools I found three of them useful and reliable:
- direvent | GNU written in C
- iwatch | in Perl
- fsniper
And from these three, the direvent is the best with no doubt.
It is so useful and reliable to use that I have used it for a simple pipeline with my microservices I have.
direvent.conf
file for watching
syslog {
facility local0;
tag "direvent";
print-priority yes;
}
watcher {
path /var/www/html/ir/jsfun/build/react recursive;
file "*.js";
event write;
command "/usr/bin/xdotool search --onlyvisible --class firefox key F5";
option (stdout, stderr, wait);
}
This is just one of the config files I have in which whenever there is a change for write then this command is executed - which could be a bash script as well.
No need to use while
loop or other stuff.
features
- recursively watching a directory
- can be run in foreground or background as a daemon
- portable. I have it in Ubuntu as well as CentOS7 server
- file name report
- regex to support file matching
- mulit-file watching (watch more then just one type of file)
- synchronize commands execution (if we have multi-watcher)
- syslog handling (log to syslog if we want)
- easy and good CLI and config file design to work with
Developed by Sergey Poznyakoff.
Installing
sudo apt install direvent
But installing it using its source code is pretty easy and straightforward which gives you the last version (right now is: 5.1).
- download
- document
Solution 3:
Using inotifywait
is the typical approach, but there's also a separate utility called inotify-hookable
that just lets you run a command when a file (or directory) changes:
inotify-hookable -f filename.adoc -c 'asciidoctor -q filename.adoc'
It seems to exit after triggering the command once; I don't see an option for continous watching, so you could do something like:
while true; do
inotify-hookable -f filename.adoc -c 'asciidoctor -q filename.adoc'
echo "== $(date) : executed, continuing to monitor..."
done
Note there are options to monitor several files or a directory, recursively, and options to ignore files/paths.