This works for me:
$ inotifywait -q -m -e close_write,create --recursive dir1/ | \
(
CNT=0;
while read -r filename event; do
echo "count: $CNT filename: $filename event: $event"; ((CNT++));
[ "$CNT" -eq 1 ] && exit;
done
)
Example
To start I made a sample directory structure to work with:
$ mkdir -p dir1/dir2/dir{3..5}
$ tree dir1/
dir1/
└── dir2
├── afile
├── dir3
├── dir4
└── dir5
4 directories, 1 file
I then ran this to start watching the directory:
$ inotifywait -q -m -e close_write,create --recursive dir1/ | ( CNT=0; while read -r filename event; do echo "count: $CNT filename: $filename event: $event"; ((CNT++)); [ "$CNT" -eq 1 ] && exit; done )
I then ran touch afile
commands in this directory:
$ cd dir1/dir2
$ touch afile
$ touch afile
These resulted in this output from the inotifywait
:
count: 0 filename: dir1/dir2/ event: CLOSE_WRITE,CLOSE afile
Once it gets to the 2nd 'event' it exits.
Problems and a better solution
One issue with the use of the subshell (...while ...)
to the pipe is that we do not see the 2nd message from echo
when the 2nd event occurs. No problem we can simply restructure things like this instead:
$ CNT=0; while read -r filename event; do echo "count: $CNT filename: $filename event: $event"; ((CNT++)); [ "$CNT" -eq 2 ] && break; done < <(inotifywait -q -m -e close_write,create --recursive dir1/)
count: 0 filename: dir1/dir2/ event: CLOSE_WRITE,CLOSE afile
count: 1 filename: dir1/dir2/ event: CLOSE_WRITE,CLOSE afile
$
Expanded:
$ CNT=0; \
while read -r filename event; do \
echo "count: $CNT filename: $filename event: $event"; ((CNT++)); \
[ "$CNT" -eq 2 ] && break; \
done < <(inotifywait -q -m -e close_write,create --recursive dir1/)
count: 0 filename: dir1/dir2/ event: CLOSE_WRITE,CLOSE afile
count: 1 filename: dir1/dir2/ event: CLOSE_WRITE,CLOSE afile
$
With a backgrounded task
If you have a task that's going to block inside the while ...
loop you can introduce a trap
to kill it, and then background it to allow the while ...
loop to process input from the inotifywait
.
Example:
$ cat ./notifier.bash
#!/bin/bash
trap 'kill $(jobs -p)' EXIT;
CNT=0
while read -r filename event; do
sleep 1000 &
echo "count: $CNT filename: $filename event: $event"; ((CNT++))
[ "$CNT" -eq 2 ] && break
done < <(inotifywait -q -m -e close_write,create --recursive dir1/)
In action:
$ ./notifier.bash
count: 0 filename: dir1/dir2/ event: CLOSE_WRITE,CLOSE afile
count: 1 filename: dir1/dir2/ event: CLOSE_WRITE,CLOSE afile
./notifier.bash: line 1: kill: (30301) - No such process
And there's no remnants of the backgrounded sleep
procs:
$ ps -eaf|grep [s]leep
$
Once last adjustment regarding the trap
that you may have noticed. When we do that kill $(jobs -p)
it throws garbage to the screen like this, sometimes:
./notifier.bash: line 1: kill: (30301) - No such process
We can clean this up like this:
trap 'kill $(jobs -p) > /dev/null 2>&1' EXIT;
Now when we run it:
$ ./notifier.bash
count: 0 filename: dir1/dir2/ event: CLOSE_WRITE,CLOSE afile
count: 1 filename: dir1/dir2/ event: CLOSE_WRITE,CLOSE afile
$
References