I'm trying to write a script that uses inotifywatch to watch for changes in a log file. If a specific message is written to the log file, it's supposed to trigger a certain function. The script currently exists in this basic form:

while inotifywait -e modify /var/log/auth.log
  alert=$(tail -n1 /var/log/auth.log | grep -E -o ".{0,7}password")
  if [[ $alert == "Failed password" ]]
   echo "FAILURE" >> test.log
  elif [[ $alert == "cepted password" ]]
   echo "LOGIN" >> test.log

Everything works fine – up until the point where the logfile watched by inotifywatch is rotated. Then it ceases to function. I presume that's because during rotation the watched file is renamed and no longer written to afterwards and a new file with the old name is created in its place which inotify has never been told to watch in the first place.

I tried to circumvent this by switching from inotifywatch to using tail -f but the same problem seems to apply there as well.

Now I realize this could probably be solved by creating a huge if-structure in which inotifywatch not only watches for modify, but also for file creation and restarts a watch for modification. But I like to keep things simple, so does anyone know if there's a simpler way? (And please, no, I don't want to use prefabricated solutions like fail2ban etc. – the interesting part for me is to create stuff like this myself with simple tools.)

  • 2
    if your tail command supports -F it should do what you want. – meuh Dec 18 '16 at 9:07

inotify is used to monitor files an directory by their inode, not by their name. When the file is rotated, its content does not change any longer (except for a short period, until the daemons are reloaded so that they use the newly created log file)

AFAIK, tail -f uses the inotify system, so it won't help. But if you have a working solution with tail -f then use tail --follow=name (or tail -F) if this is supported by your version of tail (POSIX tail does not support this). tail will then monitor the file identified by its filename. Here is an excerpt of the man page:

With --follow (-f), tail defaults to following the file descriptor, which means that even if a tail'ed file is renamed, tail will con‐ tinue to track its end. This default behavior is not desirable when you really want to track the actual name of the file, not the file descriptor (e.g., log rotation). Use --follow=name in that case. That causes tail to track the named file in a way that accommodates renaming, removal and creation.


Example of use:

tail -n0 -F my_file.log \
| while read -r log_line; do
    do_something_with "$log_line"

Because of the pipe, the while loop executes in a sub-process, which may cause you troubles if you want to modify variables outside the loop. If you use bash, you may want to use this alternative syntax which does not have this undesirable effect (but is less readable):

while read -r log_line; do
    do_something_with "$log_line"
done < <(tail -n0 -F my_file.log)
  • Very simple and nice solution, thanks! Do you also happen to know how to build that into a while-loop so that do ... done will only be triggered once per new line of the followed file? – Nick Dec 18 '16 at 10:42
  • @Nick I have amended my answer to add such an example. – xhienne Dec 18 '16 at 14:03

It can be done with inotifywait with a one-liner:

inotifywait -e move_self -e modify -m /path/to/some/file | awk '$2=="MODIFY" {system("/bin/echo Yes")}'

The -m flag keeps monitoring the file indefinitely, for the events move_self (which allows monitoring the name change of a file or directory) and modify, passing the output to an awk command which weeds out move_self events, executing the command inside the system set of parentheses for every modify event. You will have to write your own shell script in lieu of my echo command. Please pay attention to the various sets of apostrophes and quotation marks.

  • Intersting idea! But in my current experiments this - when first started - does not do your stuff, because move_self is not triggered until the first rotation takes place. Afterwards, it establishes a watch but only again for move_self and still doesn't watch for modify. Not sure why, because your code would indicate otherwise. But I'll dig in deeper from there, thanks! – Nick Dec 18 '16 at 10:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.