I want a script to sleep unless a certain file is modifed/deleted (or a file created in a certain directory, or ...). Can this be achieved in some elegant way? The simplest thing that comes to my mind is a loop that sleeps for some time before checking the status again, but maybe there is a more elegant way?
3 Answers
On linux, you can use the kernel's inotify
feature. Tools for scripting can be found there: inotify-tools.
Example use from wiki:
#!/bin/sh
EVENT=$(inotifywait --format '%e' ~/file1) # blocking without looping
[ $? != 0 ] && exit
[ "$EVENT" = "MODIFY" ] && echo 'file modified!'
[ "$EVENT" = "DELETE_SELF" ] && echo 'file deleted!'
# etc...
-
1Most unices have a similar feature. Unfortunately each has its own interface, and many have no shell API. Jan 7, 2011 at 18:40
There is an API called inotify for C programmers.
There are some tools that use it, e.g. incron and inotify-tools.
Indeed there is: entr(1) will run arbitrary commands when files change, and also provides an auto-reload option for restarting application servers.
edit: some examples
Rebuild if sources files change
$ find *.c | entr make
Launch and auto-reload test server if files change
$ ls *.py | entr -r python main.py
Providing a the agument +/path/to/fifo
allows more complex scripting by instructing entr
to write the name of each file that changes to a named pipe. The following will convert Markdown files in the current directory to HTML as they're edited
$ ls *.md | entr +/tmp/notify &
$ while read F
> do
> markdown2html $F
> done < /tmp/notify
-
Intriguing, but weird. So ... it reads a bunch of file names on standard input, and runs the specified command when one of them changes?– tripleeeJul 4, 2013 at 5:36
-
Looks interesting, thanks! Is there any way to have
entr
pass on the name of the file that has changed as well? Jul 4, 2013 at 7:00 -
As of the 2.7 release, the special
/_
argument (somewhat analogous to$
_ in Perl) is replaced with the name of the first file that changed– eradmanDec 6, 2014 at 11:50 -
@TobiasKienzler also, maybe you could use
xargs -I % entr <command> %
to pass the file name from the previous pipe offind *.ext |
or use the special argument as @eradman suggests– alchemyFeb 12, 2022 at 22:50