You are not logged in.
What I want:
I want to monitor a folder for changes, and execute various sets of commands depending on what happened to what file.
Here's a bash example:
#!/bin/bash
function main {
mkdir -p /tmp/test
touch /tmp/test/{stuff,things,whatever}.txt
inotifywait -m -e delete_self -e close_write "/tmp/test" | while read line; do
event="$(echo $line | awk '{print $2}')"
file="$(echo $line | awk '{print $3}')"
case "$event" in
CLOSE_WRITE*)
case "$file" in
stuff.txt)
# ... Do stuff here ...
echo "Doing stuff..."
;;
things.txt)
# ... Do things here ...
echo "Doing things..."
;;
whatever.txt)
# ... Do whatever ...
echo "Doing whatever..."
;;
esac
;;
DELETE_SELF*)
echo "Monitored path deleted. Exiting..."
clean_exit 1
;;
*)
echo "Unhandled event: $event"
clean_exit 1
;;
esac
done
}
function clean_exit {
#Tidy things up before exiting.
trap - SIGHUP SIGINT SIGTERM SIGQUIT
rm -r /tmp/test
}
trap clean_exit SIGHUP SIGINT SIGTERM SIGQUIT
main "$@"
My problem:
When the scripts exits for various reasons, it leaves the inotifywait process left behind.
Now one might think that I shouldn't be using the monitor mode, but doing an endless "while true" loop instead.
The problem with that is the time it takes for the set of commands for each event. Every time an event occurs, it leaves the inotifywait process and starts executing the commands for the event.
And while that is happening, the script will miss out on additional events in the folder in question. So one could agree with me that the monitoring mode is better to make sure no events are missed out.
-
So my two problems are:
Use a while true loop, and possibly miss out on important events.
Use the monitor mode, and leave inotifywait processes left in the memory every time the scripts exits.
-
What I really want:
Once the scripts exits, I want the inotifywait process to quit. Preferably when the monitored folder is deleted during the clean_exit function.
-
What I've tried so far:
I tried a simple "killall inotifywait", but that will interfere with other processes that runs inotifywait.
I tried to catch the inotifywait PID, but it's not easy when piping it. Suggestions?
Perhaps I can use the "delete_self" event, and do something fancy? Can I send commands to the existing inotifywait process?
Last edited by SysGhost (2014-09-14 13:31:30)
Offline
Use an infinite loop using command redirection:
while read line; do
...
done < <(inotifywait)
To kill the inotifywait in the end either go through and kill each child process individually, kill the pid group of the script, or try to find the pid of inotify. I'm on my mobile right now so I can't really check if this is specific to zsh, but the syntax for killing the group usually is a dash before the parent/child pid.
kill -9 -$$
You can also try to kill inotifywait specifically, although this is assuming that you do not have any other scripts running and using the inotifywait command.
kill $(pgrep inotifywait)
p.s. As a little tip, you can replace the subshell being spawned with these lines.
event="$(echo $line | awk '{print $2}')"
file="$(echo $line | awk '{print $3}')"
Just split $line into an array using " " as the delimiter.
Last edited by Saint0fCloud (2014-09-13 15:52:04)
Offline
I'm not sure what amount of events you're expecting; but pipes in linux are buffered; with a default size of 4k. That's enough to buffer a few events.
Also; this is more readable:
inotifywait -m -e delete_self -e close_write "/tmp/test" --format "%e %w" | while read event file; do
case "$event" in
[...]
esac
done
Offline
@Spider.007
Nice tip on the format option. I did read about it in the man page, but didn't recall what I've read when I wrote the example above.
It has been implemented.
EDIT (For the record): I actually needed --format "%e %f". That way it'll report event and filename.
@Saint0fCloud
kill -9 -$$
Heck, why didn't I think of that before?
But I'd avoid the -9 signal... for now. inotifywait shouldn't hang that badly.
Hopefully this ought to be enough: "kill -- -$$"
EDIT 2:
My findings so far:
Command "kill -- -$$" works like a charm.
My cleanup function now looks like this:
function cleanup {
trap - SIGHUP SIGINT SIGTERM SIGQUIT
echo -n "Cleaning up... "
rm "$pidfile" > /dev/null 2>&1;
rm -r "$workpath" > /dev/null 2>&1;
echo "Done."
kill -- -$$
exit $1
}
When the inotifywait process catches a "delete_self" event, it'll call the cleanup fuction directly.
Perfect! Now I can simply delete the working path, and the service will shut itself down properly. A bonus function.
Just for the records:
My main function looks like this:
function main {
trap cleanup SIGHUP SIGINT SIGTERM SIGQUIT
inotifywait -m -e delete_self -e close_write "$workpath" --format "%e %f" --quiet | while read event file; do
messages=""
case "${event}" in
CLOSE_WRITE*)
messages=${messages}"'${workpath}/${file}' updated. Sending data...\n"
case "${file}" in
static.xpm)
send_xpm_data s "${workpath}/${file}"
;;
flashing.xpm)
send_xpm_data f "${workpath}/${file}"
;;
rolling.xpm)
send_xpm_data r "${workpath}/${file}"
;;
animate.mng)
if [ ! -d "${workpath}/${file}.ani" ]; then
mkdir -p "${workpath}/${file}.ani"
convert_animation "${workpath}/${file}" "${workpath}/${file}.ani"
fi
send_raw_data "${workpath}/${file}.ani/*.aoled"
;;
esac
;;
DELETE_SELF*)
echo "Monitored path deleted. Exiting..."
cleanup 0
;;
*)
messages=${message}"Unhandled event: $event\n"
;;
esac
echo -e "$messages"
done
cleanup 1
}
As one might suspect, it's watching a bunch of pre-created images, convert them and send them to an OLED display upon updates.
Last edited by SysGhost (2014-09-14 15:15:35)
Offline