Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

bash script to make `tail -f` exit when the file is moved or deleted

Currently removing, moving or renaming a file that has tail -f running on it does nothing, and I'd like it to abort. I've read the man pages and it seems that -f should abort on file move and that -F will follow the file but on Mac OS X it seems -f and -F are the same. How can I write a bash script that makes tail -f exit cleanly after the file has been moved?

like image 984
schellsan Avatar asked Feb 16 '14 02:02

schellsan


2 Answers

  • On Linux, you can use tail --follow=name (rather than just -f, which is equivalent to --follow=descriptor) to achieve what you want, but ONLY if the file is DELETED rather than moved - once the file is deleted, an error message is reported and tail exits (with code 1); sadly, by contrast, if the file is merely MOVED (renamed), tail does NOT exit - necessitating a programmatic solution.
  • On OSX, you always need a programmatic solution - whether the file is moved or deleted.

bash script for exiting tailing once the target file no longer exists (under its original name) - more robust formulations of the script from @schellsan's own answer:

#!/usr/bin/env bash

tail -f "$1" &  # start tailing in the background
while [[ -f $1 ]]; do sleep 0.1; done # periodically check if target still exists
kill $! 2>/dev/null || : # kill tailing process, ignoring errors if already dead
  • Deals correctly with filenames that need quoting (e.g., names with embedded spaces).
  • Prevents creating a tight loop by sleeping between the file-existence checks - adjust the sleep duration as desired; caveat: some platforms only support integral seconds.

If more robustness is desired, here's a version that:

  • kills the background process via an exit trap, so as to ensure that it is killed, regardless of how the script itself exits (normally, or, say, via Control-C).
  • exits the script if the background process is found to no longer be alive.
#!/usr/bin/env bash

# Set an exit trap to ensure that the tailing process
# - to be created below - is terminated, 
# no matter how this script exits.
trap '[[ -n $tailPid ]] && kill $tailPid 2>/dev/null' EXIT

# Start the tailing process in the background and
# record its PID.
tail -f "$1" & tailPid=$!

# Stay alive as long as the target file exists.
while [[ -f $1 ]]; do
  # Sleep a little.
  sleep 0.1
  # Exit if the tailing process died unexpectedly.
  kill -0 $tailPid 2>/dev/null || { tailPid=; exit; }
done
like image 133
mklement0 Avatar answered Sep 23 '22 12:09

mklement0


Just in case anyone else runs into this problem you can use a small script in which you run tail as a background process and then loop until the file is moved, killing the tail process.

#!/bin/bash

tail -f $1 &
pid=$!

while [ -f $1 ]
do
    if [ ! -f $1 ]
    then
        kill -9 $pid
    fi
done
like image 33
schellsan Avatar answered Sep 21 '22 12:09

schellsan