Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I interrupt or debounce an inotifywait loop?

I have a little script that watches files for changes using inotifywait. When something changes, a batch of files are sent through a process (compiled, compressed, reorganised, etc) that takes about ten seconds to run.

Consider the following example:

touch oli-test
inotifywait -mq oli-test | while read EV; do sleep 5; echo "$EV"; done

If you run touch oli-test in another terminal a few times, you'll see that each loop completes before it moves on. That scenario is very real to me. If I forget to save a file while it's already processing, or notice a mistake, the events stack up and I'm waiting minutes.

It strikes me that there are two techniques that would make this workflow objectively better. I'm not sure what is easiest or best, so I'm presenting both:

  1. Interrupt previous run-throughs, and restart immediately. The scripted process is currently just an inline set of commands. I could break them out to Bash functions, I'm not wild about breaking them further out than that.

  2. Debounce the list of things waiting to be processed so that if five events happen at once (or while it's already processing), it only runs once more.

(Or both... because I'm certain there are cases where both would be useful)

I am also open to approaches that are different from inotifywait but they need to give me the same outcome and work on Ubuntu.

like image 980
Oli Avatar asked Jan 28 '15 15:01

Oli


2 Answers

To interrupt, you can shift things around so the processing runs in a background subshell and each new inotifywait event nukes the background processes:

inotifywait -mq oli-test | while read EV; do
    jobs -p | xargs kill -9
    (
        # do expensive things here
        sleep 5  # a placeholder for compiling
        echo "$EV"
    ) &
done
like image 74
Oli Avatar answered Oct 14 '22 08:10

Oli


Here is a compact solution:

inotifywait -q -m -e modify -e create -e close_write --format "%w%f" /etc/nginx/ |\
while read -r path; do
    echo $path changed
    echo "Skipping $(timeout 3 cat | wc -l) further changes"
    service nginx reload
done

The first read waits for a line of data, so this won't eat your CPU. The timeout 3 cat reads any further change notifications that come in over the next 3 seconds. Only then does nginx get reloaded.

like image 21
Max Murphy Avatar answered Oct 14 '22 09:10

Max Murphy