Auto-running a script when a file changes with inotifywait
I was working on a really tiny script that outputs a file.
The familiar development cycle on something like this is:
-
Save changes to my script
-
Run the script
-
Re-load the file to see if it’s right
But I got real tired of that and thought it would be neat to have the script re-run when I make changes to it so all I would have to do is save and then check the output file.
inotify-tools
Linux has an inotify
API for monitoring filesystem (inode) events.
Slackware has the userland inotify-tools
package installed by default.
In particular, inotifywait
, which sleeps until a particular event
occurs on a file (or set of files).
We can put that in a loop and wait for a modify
event.
It can even be a one-liner:
while true; do inotifywait -e modify foo.txt; echo "Change!"; done
Open a pair of terminals and try it out:
TERMINAL ONE TERMINAL TWO ====================== ========================== $ touch foo.txt $ while true; do ... Setting up watches. Watches established. foo.txt MODIFY $ echo 'hello' > foo.txt Change! Setting up watches. Watches established. foo.txt MODIFY $ echo 'world' >> foo.txt Change!
Changing files in Vim doesn’t appear to work?
UPDATE: I think the below may be wrong! I think Vim may
choose to write the file based on how long it’s been open
or how many changes have been made or something else.
It doesn’t appear that :w!
does what I thought it did.
(Which explains why it doesn’t explicitly say so in the help doc.)
Today I learned that not only does Vim store changes in swap files while you’re
editing, it also does not overwrite the target file when you write (:w(rite)
) the
file. I’m surprised I didn’t know this.
Anyway, I kept typing :w
and inotifywait e modify
wasn’t seeing my changes.
Web searches are busted in 2025 and I am very sad about that. So I found
the answer in Vim with :help :write
:
:w[rite]! Like ":write", but forcefully write when 'readonly' is set or there is another reason why writing was refused. Note: This may change the permission and ownership of the file and break (symbolic) links. Add the 'W' flag to 'cpoptions' to avoid this.
It doesn’t say it explicitly, but the "may change the permission and ownership…" part was the clue I needed. I believed that implied the "bang" version writes to the target file. And it appears to work!
So, the answer is: you have to write with :w!
, not just :w
and the
inotifywait command will see it.
Again, the above appears to be wrong!
My script
Here’s my entire script. Just like the one-liner, it waits in a loop and performs an action every time the target file is modified.
#/bin/sh # Watch changes to shape.rb (during development) # Run it on itself and write output to .svg file. # Uses 'inotifywait' from 'inotify-tools' # (installed by default on Slackware Linux) WATCHME='shape.rb' OUTPUT='test.svg' echo "Watching $WATCHME for changes..." echo "(Quit with Ctrl+C. Save with ':w!')" while true do # hush the info from inotifywait on stderr inotifywait -e modify $WATCHME 2> /dev/null ruby $WATCHME $WATCHME > $OUTPUT echo "$OUTPUT written!" done