This is a card in Dave's Virtual Box of Cards.

Auto-running a script when a file changes with inotifywait

Page created: 2025-06-10

I was working on a really tiny script that outputs a file.

The familiar development cycle on something like this is:

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