I would like to follow changes to a file via tail -f and then print out the first line that matches a grep command before terminating. What's the simplest way to do this? So far I've been experimenting with things like:

tail -f foo.log | grep 'bar' | head -1

However, the pipeline just hangs, presumably due to buffering. I also tried

tail -f foo.log | grep --line-buffered 'bar' | head -1

This prints out the line, but the process does not terminate unless I hit ^C, presumably because a second line of input is needed to terminate head -1. What's the best way to solve this problem?

link|improve this question

feedback

3 Answers

up vote 6 down vote accepted
tail -f foo.log | grep -m 1 bar

if the file foo.log is writen rarily to, you can do:

grep -m 1 bar <( tail -f foo.log )

It should be noted that the tail -f will stay in background until it will get another line to output. If this takes long time it might be problematic. solution in such case is:

grep -m 1 bar <( exec tail -f foo.log ); kill $! 2> /dev/null

kill will kill leftover tail -f process, and we hide errors, because it's possible that the tail will be gone by the time kill will be invoked.

link|improve this answer
Doesn't work for me. – PriceChild Apr 27 '11 at 10:43
What do you mean "doesn't work for me". Is there any error message? Anything happens? Does your grep support -m option? – depesz Apr 27 '11 at 10:45
Bah sorry I was too late on the edit. It doesn't terminate for me. – PriceChild Apr 27 '11 at 10:52
This means that either the grepped string is not there, or the pipe to grep is buffered. test with this: perl -e '$|++; my $i = "a"; while (1) { print $i, "\n"; $i++; select undef, undef, undef, 0.1}' | grep -m 1 aa - every 0.1 second it prints next string, first "a", then "b", ... "z", "aa", "ab" ... - grep finishes after "aa", so it should be ~ 2.7 second after start. and it works. the problem might be if you have something else between tail -f .. and | grep - like another grep, sed or something like this. – depesz Apr 27 '11 at 11:23
Oh... I've been testing on auth.log, grepping for 'a'. If there's an initial match it seems its not terminating until the file gets a little more appended to.. – PriceChild Apr 27 '11 at 11:32
show 4 more comments
feedback

It's as easy as: tail -1 foo.log

link|improve this answer
2  
I believe the OP would like to print the first entry which matches a regex, hence the use of grep in the examples above. – heavyd Apr 27 '11 at 4:22
Yes, I'd like to be able to grep for some text, print the first matching line, and terminate. – jonderry Apr 27 '11 at 6:26
feedback

I think that applying unbuffer like this,

tail -f foo.log | unbuffer -p grep 'bar' | head -1

would work. I'm not at a system where I can test it, though, and I can't justify why it would work while grep --line-buffered does not. However, I did try this alternative which does seem to work:

tail -f foo.log | sed -n '/bar/{p;q}'

When the match to bar is found, p prints it and q immediately quits.

link|improve this answer
Neither work for me. (They don't terminate) – PriceChild Apr 27 '11 at 10:50
feedback

Your Answer

 
or
required, but never shown

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