Why isn't `tail -f … | grep -q …` quitting when it finds a match?

I'm doing a pretty standard tail + grep:

tail -f some_log_file.txt | grep -q known-string

If I run the command without the -q:

tail -f some_log_file.txt | grep known-string

I see output:

[Tue Feb 12 11:32:45 2019] known-string.

so I know the grep is matching. However when I add -q the grep command doesn't exit, it just hangs there waiting for more output ... even though the man page says it will "Exit immediately with zero status if any match is found":

   -q, --quiet, --silent
          Quiet;  do  not  write  anything  to  standard   output.    Exit
          immediately  with  zero status if any match is found, even if an
          error was detected.  Also see the -s or --no-messages option.

Can anyone explain why -q isn't causing my grep to exit? I'm trying to chain a && beep to the end so the grep beeps when a match is found, but unless I can make it exit that won't work.


From StackOverflow post 'grep -q' not exiting with 'tail -f':

tail -f will read a file and display lines later added, it will not terminate (unless a signal like SIGTERM is sent). grep is not the blocking part here, tail -f is. grep will read from the pipe until it is closed, but it never is because tail -f does not quit and keep the pipe open.


A solution to your problem would probably be (not tested and very likely to perform badly):

tail -f logfile | while read line; do
  echo $line | grep -q 'find me to quit' && break;
done

You will find more information and solutions in the linked post.


grep does exit and the pipe goes away, although tail keeps running. This bug report log starts with a use case very similar to yours:

I want to use tail and grep to follow a file until a particular pattern appears. But tail does not exit when grep is finished.

$ echo xxx > /tmp/blabla
$ tail -f /tmp/blabla |grep -m1 --line-buffered "xxx"
xxx

Now tail still tries to read and exits only if I write again into /tmp/blabla.

Is this how it's supposed to be?

The explanation there:

tail does exit on SIGPIPE, however it will only get the signal on write(), and so you need to get more data in the file before tail will exit.

As far as I know, this exact mechanism is very common. Many tools exit after they try to write something to a broken pipe, it's not a bug.

Then this wish came:

It's a fair point though that tail, since it can hang around forever should take special steps to be responsive to the other end of the pipe going away.

And finally:

Implemented in:
https://git.sv.gnu.org/cgit/coreutils.git/commit/?id=v8.27-42-gce0415f

And indeed, when I try to reproduce your problem with tail from GNU coreutils 8.28, I cannot. The tool exits immediately.