Catching own tail

22.02.2010 16:31

How to make a shell (er, Bash) script wait until a certain line appears in a log file? Sounds simple, but I have yet to find an elegant solution for this task.

A common use case for this is when you start a daemon that forks into background and you need to wait in the script until the daemon has finished doing something.

The following is the best I came up with:

tail -f $LOG | ( \
	IFS=""
	while read LINE; do
		if echo "$LINE" | grep "$CANARY" > /dev/null; then
			break
		fi
	done
	pkill -f "tail -f $LOG"
)

IFS is unset here because it appears to help with buffering for some reason. Without that line the script will sometimes wait even after the $CANARY has been appended to the file. That can be problematic when the line you're looking for is the last one that will be written to the log.

The most obvious flaw here is that pkill will kill all tail processes, even those that have not been started from this script.

Any better solutions are most welcome.

Update: Thanks to Nace here's a better version of the script that is more carefull at killing the tail process:

PARENT="$BASHPID"                 # (Bash 4.x)
PARENT=`$SHELL -c 'echo $PPID'`   # (Bash 3.x)
tail -f $LOG | ( \
	IFS=""
	while read LINE; do
		if echo "$LINE" | grep "$CANARY" > /dev/null; then
			break
		fi
	done
	pkill -P "$PARENT" -xf "tail -f $LOG"
)
Posted by Tomaž | Categories: Code

Comments

tail -f $LOG | ( \
parent=$$
IFS=""
while read LINE; do
if echo "$LINE" | grep "$CANARY" > /dev/null; then
break
fi
done
pkill -P $parent -f "tail -f $LOG"
)

This will kill the tail process only if it was started by the same bash shell as this script.

Posted by Nace Oroz

Argh, my comment got broken. This is how it should look like - http://www.pastebin.com/f2bc474c0

Posted by Nace Oroz

Thanks Nace.

There is one problem with your solution. If you run this code from with-in a Bash function, which is itself executed in a subshell, $$ returns the PID of the top-most shell. While the tail's parent is the subshell executing the function.

It appears that "pkill -P" only checks against the immediate parent PID, so the "tail" is never killed.

Posted by Tomaž

Yep, does not work in bash function. I'll try to search for a better solution. :)

Posted by Nace Oroz

Ok, mystery solved. :)

Here is the solution: http://pastebin.com/Vt2YuqXt

Posted by Nace Oroz

Thanks.

I also figured out how to make it work on Bash 3.x ($BASHPID was introduced in 4.0) - see the update in the post above.

Posted by Tomaž

Add a new comment


(No HTML tags allowed. Separate paragraphs with a blank line.)