7

I want to watch a file until a bit of text appears

I found this answer: `tail -f` until text is seen

but when I tried it on ubuntu, it doesn't exit:

$ echo one > test.txt
$ echo two >> test.txt
$ echo three >> test.txt
$ echo four >> test.txt
$ cat test.txt | sed '/w/ q'
one
two
$

works as expected. however when I try to tail the file

$ tail -f test.txt | sed '/w/ q'
one
two

it never exits. tail does not stop even though the pipe is broken.

Does anybody know how to make tail exit when sed exits?

4
  • 1
    Similar: unix.stackexchange.com/a/366806/22565 Commented Jan 10, 2018 at 16:51
  • I don't think either of those are similar to this question. One is about writing your own program (in golang) that exits when the pipe is broken. This question is specifically about tail -f and asking why something in a previous answer doesn't work.
    – Alex028502
    Commented Jan 10, 2018 at 17:15
  • The answers there explain why tail -f is not killed when sed exits, when the pipe is broken (because it doesn't write anything after) and how to work around it. Commented Jan 10, 2018 at 17:16
  • funny thing is what I am trying to do is an accepted answer here: unix.stackexchange.com/a/45943/158192
    – Alex028502
    Commented Jan 10, 2018 at 17:20

1 Answer 1

10

It's the same things as in:

In:

cmd1 | cmd2

Your shell happens to wait until cmd1 terminates even after cmd2 has already terminated. That's not the case of all shells. Some like the Bourne or Korn shell don't for instance.

When cmd2 dies, the pipe on cmd1's stdout becomes broken, but that doesn't terminate cmd1 instantly.

cmd1 will terminate the next time it tries to write to that pipe. It will then receive a SIGPIPE whose default action is to terminate the process.

With cmd1 == tail -f file and cmd2 == sed /w/q, tail -f will read the last 10 lines of the file and write them to stdout (the pipe), typically in one chunk unless the lines are really big and sit there waiting for more text to be appended to file.

sed, which runs concurrently, will wait for input on its stdin, read it, process it line by line and quit if there's a line that contains w.

As soon (or possibly with a one-line delay with some sed implementation) as it finds that line, it exits, but at that time, tail has already written to the pipe all it had to WRITE, so it won't receive a SIGPIPE unless some additional text is added later on to the file (at which point it will do the fatal write()).

If you wanted cmd1 to terminate as soon as cmd2 terminates, you'd need something to kill it once cmd2 terminates. Like with:

sh -c 'echo "$$"; exec tail -f test.txt' | {
  IFS= read pid
  sed /w/q
  kill -s PIPE "$pid"
}

Or with bash:

{ sed /w/q; kill -s PIPE "$!"; } < <(exec tail -f text.txt)
  

2020 Edit

As noted by @user414777, since version 8.28, the GNU implementation of tail, in follow modes, now not only polls for new data in the file(s) it monitors but also checks whether its stdout becomes a broken pipe and exits straight away then (or within one second if inotify is not used), making those work arounds above unnecessary.

However note that only GNU tail does this kind of processing, not any other implementation of tail (AFAIK), and not any other utility (even the GNU implementations).

So, while:

tail -f file | head -n1

Will exit after one line,

tail -f file | tr '[:lower:]' '[:upper:]' | head -n1

for instance will not necessarily as tr will not monitor whether its stdout becomes a broken pipe, and so will only die if it writes something there after the pipe has broken as usual (and it's only at that point that GNU tail -f will exit).

6
  • +1! very useful for tailing a file until a message happens (such as waiting for a deploy to finish): { awk '{print $0};/has finished in/{exit}' ; kill -s PIPE "$!" ; } < <(exec tail -f /var/log/tomcat8/catalina.out) Commented Jul 6, 2018 at 20:04
  • Note that mawk buffers output by default. If you are on debian/ubuntu, you want to either use gawk or pass the -Winteractive flag to mawk for this trick to work. See perkin.org.uk/posts/how-to-fix-stdio-buffering.html Commented Jul 12, 2019 at 6:20
  • @JonasDahlbæk all awk implementations buffer their output when it doesn't go to a terminal. What's unique to mawk is that it buffers its input (accumulates a block full before starting to process it) Commented Jul 12, 2019 at 16:50
  • You are right, of course. Commented Jul 12, 2019 at 18:02
  • 2
    See the answer here about the special thing GNU tail does so that the tail -f | sed /pat/q (sometimes) works.
    – user313992
    Commented Oct 25, 2020 at 22:15

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .