views:

995

answers:

4

Hi All,

I'm trying to write a (sh -bourne shell) script that processes lines as they are written to a file. I'm attempting to do this by feeding the output of tail -f into a while read loop. This tactic seems to be proper based on my research in Google as well as this question dealing with a similar issue, but using bash.

From what I've read, it seems that I should be able to break out of the loop when the file being followed ceases to exist. It doesn't. In fact, it seems the only way I can break out of this is to kill the process in another session. tail does seem to be working fine otherwise as testing with this:

touch file
tail -f file | while read line
do
  echo $line
done

Data I append to file in another session appears just file from the loop processing written above.

This is on HP-UX version B.11.23.

Thanks for any help/insight you can provide!

A: 

I don't know about HP-UX tail but GNU tail has the --follow=name option which will follow the file by name (by re-opening the file every few seconds instead of reading from the same file descriptor which will not detect if the file is unlinked) and will exit when the filename used to open the file is unlinked:

tail --follow=name test.txt
Robert Gamble
Hi Robert. I did see this option in my Google searches, and it sounded like the best option. Unfortunately, HP-UX doesn't support this option.
AgentConundrum
@Agent - Can you download and compile (port) GNU tail? It certainly seems to be what you want.
tvanfosson
@tvanfosson - This is sort of a fun side project to emulate a nice bit of functionality from the system (MPE) we just ported from. It's just to make the devs lives a bit easier. I doubt I could get approval to compile the GNU tail in dev, let alone production (would be a useful tool for oncall work)
AgentConundrum
+1  A: 

If you want to break out, when your file does not exist any more, just do it:

 test -f file || break

Placing this in your loop, should break out.

The remaining problem is, how to break the read line, as this is blocking.

This could you do by applying a timeout, like read -t 5 line. Then every 5 second the read returns, and in case the file does not longer exist, the loop will break. Attention: Create your loop that it can handle the case, that the read times out, but the file is still present.

EDIT: Seems that with timeout read returns false, so you could combine the test with the timeout, the result would be:

  tail -f test.file | while read -t 3 line || test -f test.file; do 
          some stuff with $line
  done
flolo
This will keep processing the most recent line every 3 seconds if no new data is being added to the file.
Robert Gamble
Thats why I wrote: "Attention: Create your loop that it can handle the case, that the read times out, but the file is still present."
flolo
That sentence is very vague and doesn't make the problem with this solution clear.
Robert Gamble
A: 
Cirno de Bergerac
A: 

I've never been happy with this answer but I have not found an alternative either:

kill $(ps -o pid,cmd --no-headers --ppid $$ | grep tail | awk '{print $1}')

Get all processes that are children of the current process, look for the tail, print out the first column (tail's pid), and kill it. Sin-freaking-ugly indeed, such is life.

rektide