12

I'm writing a bash script to use inotifywait to monitor a directory and kick off actions when changes are detected. Something like:

inotifywait -m ... | while read f; do something; done

Since inotifywait doesn't terminate by itself, this script will not halt.

So my plan was to get the PID of the inotifywait process, save it to a file and have a different process kill it later, say like:

inotifywait -m ... | { echo ??PID?? > pid-file; while ... }

But I don't know how to get the PID. Is there a simple way to achieve this? Another way is just to save the PID of the shell-script $$ to the file and kill the entire shell-script but I wanted to do some cleanup after the while loop.

I have tried using coproc and I think it will work but it seems like more complication than necessary.

  • You could use something like this ps -ef | grep processName | grep -v grep | awk '{print $2}' | xargs kill -9 – Kiwy Dec 03 '13 at 09:13
  • @Kiwy - instead of that mess just do a pgrep inotifywait. That will give you the PID,to kill, pkill inotifwait. – slm Dec 03 '13 at 12:42
  • @slm depending of your system you will not have pgrep and pkill while grep and ps while almost be present. You're welcome – Kiwy Dec 03 '13 at 12:48
  • @Kiwy - doubtful, those tools are pretty ubiquitous. Also you do not need to do a grep -v grep, instead ps -ef | grep [p]rocessname... would do the same. – slm Dec 03 '13 at 13:10
  • @Kiwy - yes and given we're talking about inotifywait I believe that's only a Linux technology or at the very least not present on AIX. I'm not trying to be insulting and if I did I apologize, just trying to be concise. – slm Dec 03 '13 at 15:52
  • @slm, although your method looks cleaner, it is possible that pgrep won't do the job. Refer to http://askubuntu.com/questions/157075/why-does-ps-aux-grep-x-give-better-results-than-pgrep-x - pgrep only looks at the first 15 characters and so if inotifywait is listed as /usr/local/bin/inotifywait for example, pgrep won't work while Kiwy's method will work. – Davidson Chua Dec 04 '13 at 01:06
  • 1
    @DavidsonChua - yes you can use the -f switch if you need to match against more of the executables name. – slm Dec 04 '13 at 01:08
  • Ah, that's a good point. Perhaps you would like to edit your commands in your earlier comment to pgrep -f inotifywait? – Davidson Chua Dec 04 '13 at 01:11
  • related: https://stackoverflow.com/questions/1652680/how-to-get-the-pid-of-a-process-that-is-piped-to-another-process-in-bash – Ciro Santilli OurBigBook.com Apr 09 '18 at 10:20

3 Answers3

6

In a pipeline, all processes are started concurrently, there's not one that is earlier than the others.

You could do:

(echo "$BASHPID" > pid-file; exec inotifywait -m ...) | while IFS= read -r...

Or portably:

sh -c 'echo "$$" > pid-file; exec inotifywait -m ...' | while IFS= read -r...

Also note that when the subshell that runs the while loop terminates, inotifywait would be killed automatically the next time it writes something to stdout.

Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
4

If you need the process ID in the loop, print it first.

sh -c 'echo "$$"; exec inotifywait -m ...' | {
  read inotifywait_pid
  while IFS= read -r f; do
    …
    if …; then kill "$inotifywait_pid"; break;
  done
}
1

This SO answer seems applicable:

inotifywait -m file > >(while read f; do echo f; done) &