Breaking "tail -f" that is read by "while read" loop in HP-UX
I am trying to write a (sh -bourne shell) script that processes lines as they are written to a file. I am trying to do this by feeding the output tail -f
into a loop while read
. This tactic seems to be correct, based on my research on Google and also this question , which addresses a similar problem, but using bash.
From what I've read, it seems like I have to break out of the loop when the executable ceases to exist. This is not true. Actually, it seems the only way I can get out of this is by killing the process in another session. tail
seems to work fine, otherwise testing with this:
touch file tail -f file | while read line do echo $ line done
The data I add to file
in another session is displayed as soon as the file from the above processing loop.
This is HP-UX B.11.23 version.
Thanks for any help / understanding you can provide!
source to share
If you want to break out when your file no longer exists, just do it:
test -f file || break
Placing this in your loop should break out.
The rest of the problem is how to split the read line as it blocks.
This can be done using a timeout like read -t 5 line. Then every 5 seconds, the read returns, and in case the file no longer exists, the loop is interrupted. Note: Create your loop so it can handle the case that the read time is not working but the file is still present.
EDIT: It seems that with a timeout, read returns false, so you can combine the test with a timeout, the result is:
tail -f test.file | while read -t 3 line || test -f test.file; do
some stuff with $line
done
source to share
I don't know about HP-UX tail
, but GNU tail
has a parameter --follow=name
that will follow the file by name (by reopening the file every few seconds instead of reading from the same file a descriptor that won't detect if the file is detached) and exits when the file name used to open the file will be disabled:
tail --follow=name test.txt
source to share
If you are not using GNU tail , it will not be able to complete its action by following the file. The -f option is really only for interactive monitoring - indeed, I have a book that says "-f" is unlikely to be used in shell scripts. "
But to solve the problem, I'm not entirely sure if this is not a very difficult way to do it, but I figured that you can send tail to a FIFO, then have a function or script that would check the file for existence and kill off the tail if it was detached ...
#! / bin / sh sentinel () { while true do if [! -e $ 1] then kill $ 2 rm / tmp / $ 1 break fi done } touch $ 1 mkfifo / tmp / $ 1 tail -f $ 1> / tmp / $ 1 & sentinel $ 1 $! & cat / tmp / $ 1 | while read line do echo $ line done
There has been some naive testing and it looks like everything is fine and don't leave garbage around you.
source to share
I've never been happy with this answer, but I haven't found an alternative:
kill $(ps -o pid,cmd --no-headers --ppid $$ | grep tail | awk '{print $1}')
Get all processes that are children of the current process, find the tail, print the first column (tail pid) and kill it. Sin-damn-ugly indeed, that's life.
source to share
The following approach uses the command tail -f file
echos its process id plus a custom string prefix (here tailpid:
) in a loop while
where a custom string prefix string starts another (given one) while
that checks every 5 seconds if more exists file
. If not, it tail -f file
will be killed and the subshell containing the background loop while
will exit.
# cf. "The Heirloom Bourne Shell",
# http://heirloom.sourceforge.net/sh.html,
# http://sourceforge.net/projects/heirloom/files/heirloom-sh/ and
# http://freecode.com/projects/bournesh
/usr/local/bin/bournesh -c '
touch file
(tail -f file & echo "tailpid: ${!}" ) | while IFS="" read -r line
do
case "$line" in
tailpid:*) while sleep 5; do
#echo hello;
if [ ! -f file ]; then
IFS=" "; set -- ${line}
kill -HUP "$2"
exit
fi
done &
continue ;;
esac
echo "$line"
done
echo exiting ...
'
source to share