2

I want to terminate the tail command after having the match found and until then all the lines are written to another file.

tail -f logfile | sed '/special string/q'  | tee output.txt

Output.txt will have the lines until special string is found. But the problem is, after that I want to end the tail command.

  • 6
  • In my experimentation, it seems like tail -f hangs around only until its sleep interval ends. So, in order to make it terminate faster, invoke it with --sleep-interval=N, e.g. tail -f -s 0.01 logfile | sed '/special string/q' | tee output.txt. – Amir Oct 20 '20 at 06:35
  • my script still is in the same line, it is not terminating there. @Amir – Abhay Singh Oct 20 '20 at 06:43
  • Is your pattern, special string, matching anything in the log file when interpreted as a regular expression? – Kusalananda Oct 20 '20 at 07:47
  • yes, it matches and writes the lines to output.txt file till "special string" matches. But tail -f function , I thinks still is not terminated., because the program is struck on this line. – Abhay Singh Oct 20 '20 at 07:53
  • Please provide actual data and the actual pattern that you are using. Don't provide placeholder values. Since at least three people have asked about this specific thing today, we must assume that it is a school assignment. As a school assignment, the data probably does not contain personal details that needs to be censored. – Kusalananda Oct 20 '20 at 08:21
  • { sed '/.special string/q' | tee "$output" ; kill -s PIPE "$!"; } < <(exec tail -f logfile) gives me the solution for the problem @kusalananda – Abhay Singh Oct 20 '20 at 09:05
  • @AbhaySingh If you're using bash, then mention that in the question. If you have found a solution, then post it as an answer along with an explanation of the issue and of how your solution solves the issue. – Kusalananda Oct 20 '20 at 09:07
  • You may also want to mention what Unix you are using. – Kusalananda Oct 20 '20 at 10:46
  • 2
  • @glenn does it matter? do you know any reliable way from a pipe reader to kill the pipe writer, which does not depend on /proc filesystem, does not assume that they're both part of the same process group, does not assume that pipes aren't implemented with sockets (as in ksh), and will happen immediately, not when the writer tries to write something else (which may never come). –  Oct 25 '20 at 18:28
  • use a different command in place of sed, one that will terminate when done. When it terminates the pipes will be closed, and tail will exit. consider awk. – ctrl-alt-delor Nov 17 '20 at 20:37

2 Answers2

6

You should kill it explicitly

seq 1 10 > file
tail -f file | { sed /7/q; pkill -PIPE -xg0 tail; } | tee output

pkill -PIPE -xg0 tail means

send a SIGPIPE signal to the process named exactly tail from the same process group as ourselves.

This assumes that there is no other tail running in the same process group. If the command is run from an interactive terminal (from a shell with job control), it should be safe, as each pipeline is then run in its own process group (aka job). In a shell without job control (e.g. in a script), we could wrap the pipeline in a separate shell where the job control is explicitly turned on:

sh -mc 'tail -f file | { sed /7/q; pkill -PIPE -xg0 tail; }' | tee output

But GNU tail kills itself

If you're using a Linux machine with bash and coreutils, you will notice that everything falls into place, and there's no need of any kill whatsoever; tail will simply terminate by itself:

debian$ tail -f file | sed /2/q
1
2
debian$ # WOW!

That's because the tail from GNU coreutils is using a smart trick to determine if its stdout is still writable: it's polling it for a "ready for reading" condition, which on the writing end of a pipe will only happen in case of error, as when its other end has been closed. If that's the case, then tail just kills itself with a SIGPIPE signal. Quoting from its source code:

  FD_SET (STDOUT_FILENO, &rfd);

/* readable event on STDOUT is equivalent to POLLERR, and implies an error condition on output like broken pipe. */ if (select (STDOUT_FILENO + 1, &rfd, NULL, NULL, &delay) == 1) die_pipe ();

[In fact, other systems may POLLHUP or POLLHUP|POLLIN instead of POLLERR, but this doesn't matter in practice]

GNU tail only does this on pipes, not on sockets or ttys (which means that that doesn't work with ksh93, which is using kneecapped unix-domain sockets to implement its "pipes").

Also (AFAIK) only GNU tail does this at all, and only since version 8.28; even on Linux, busybox tail does not.

That means that using tail -f | quit_at_some_point (from many answers here) is still very much hit-and-miss, and may actually never terminate.

2

Given the following logfile:

one
two
three
special string
four

And the following command

$ tail -f logfile | sed '/special string/q'  | tee output.txt

I get the following output:

one
two
three
special string

And then both tail and sed exit successfully. So your own example, at least on my computer, works exactly as you described. To further verify this, given the following file

one
two
three
four

I get no output, and the command hangs, waiting for the special string. When I, then, add it using the command echo special string >> logfile from a separate terminal, the original command succesfully exits again.

So everything seems to work correctly, right? If not, please let me know what you expected. In any case, thank you for learning me a cool new trick which I will undoubtedly use in the future!

  • 2
    It works for you because the tail from coreutils (the default on Linux) does a very smart select() trick to determine if its stdout pipe is still writable , and if it's not, it kills itself with SIGPIPE. That's unfortunately not the case everywhere. –  Oct 25 '20 at 19:08
  • Interesting! Where can I learn more about this trick? – Jaap Joris Vens Oct 25 '20 at 19:19
  • See the source code. Unfortunately, that doesn't work with a shell like ksh93, which is using Unix sockets to implement its pipes. –  Oct 25 '20 at 19:23