3

I'm writing a barrier to stall the execution of a script until a certain keyword is logged. The script is pretty simple:

tail -F -n0 logfile.log | while read LINE; do
    [[ "$LINE" == *'STOP'* ]] && echo ${LINE} && break;
done

or

tail -F -n0 logfile.log | grep -m1 STOP

The thing is it doesn't quit as soon as the keyword is detected, but only after the next line is written. I.e:

printf "foo\n"  >> logfile.log  # keeps reading
printf "foo\n"  >> logfile.log  # keeps reading
printf "STOP\n" >> logfile.log  # STOP printed
printf "foo\n"  >> logfile.log  # code exits at last

Unfortunately I can't rely on the fact that another line will be logged after the "STOP" (not within an interval useful for my purposes at least).

The workaround found so far is to tail also another file I know for sure gets updated quite frequently, but what is the "clean" solution so that the code will exit right after it logs STOP?

Cavaz
  • 2,996
  • 24
  • 38
  • Maybe the process that writes to the log buffers its output? – choroba Oct 15 '18 at 09:21
  • maybe, but the line is printed right away, so it's like it gets stuck between the `echo` and the `break`. With a buffer would expect the `echo` to be delayed too – Cavaz Oct 15 '18 at 09:32
  • You only terminate when `tail` tries to write again to the pipe, but since the pipe is broken (because your `while` loop is already terminated) it will crash. It only assesses the condition of the `pipe` when it tries to write to it. don't use a pipe! – kvantour Oct 15 '18 at 10:12

1 Answers1

8

In , when executing a command of the form

command1 | command2

and command2 dies or terminates, the pipe which receives /dev/stdout from command1 becomes broken. This, however, does not terminate command1 instantly.

So to achieve what you want is to use process substitution and not a pipe

awk '/STOP/{exit}1' < <(tail -f logfile)

When you use , you can see the behaviour in a bit more detail:

$ touch logfile
$ tail -f logfile | awk '/STOP/{exit}1;END{print "end"}'

This awk program will check if "STOP" is seen, and if not print the line again. If "STOP" is seen it will print "end"

When you do in another terminal

$ echo "a" >> logfile
$ echo "STOP" >> logfile
$ echo "b" >> logfile

You see that prints the following output:

a             # result of print
end           # awk test STOP, exits and executes END statement

Furthermore, if you look more closely, you see that is at this point already terminated.

ps before sending "STOP":

13625 pts/39   SN     0:00  |        \_ bash
32151 pts/39   SN+    0:00  |            \_ tail -f foo
32152 pts/39   SN+    0:00  |            \_ awk 1;/STOP/{exit}1;END{print "end"}

ps after sending "STOP":

13625 pts/39   SN     0:00  |        \_ bash
32151 pts/39   SN+    0:00  |            \_ tail -f foo

So the awk program terminated, but tail did not crash because it is not yet aware the pipe is broken as it did not attempt to write to it.

When you do the following in the terminal with the pipeline, you see the exit status of tail:

$ echo "${PIPESTATUS[0]} ${PIPESTATUS[1]}"
$ 141 0

Which states that awk terminated nicely, but tail terminated with exit code 141 which means SIGPIPE.

kvantour
  • 25,269
  • 4
  • 47
  • 72
  • The value of $? following your suggestion `awk '/STOP/{exit}1' < <(tail -f logfile)` seems to not reflect a failure in the tail process itself. For example, if logfile doesn't exist, awk doesn't find the word STOP, and therefore the program terminates successfully. You discuss the different exit codes when using pipes, but could you similarly expand on the different exit codes accessible when using your command substitution example? – ricky116 Dec 31 '21 at 10:15
  • To respond to my own comment, and for others with a similar query, a reasonable workaround is to do `awk '/STOP/{exit}1' < <(tail -f logfile || echo "STOP:$?")`. This means if the tail command fails, the awk process will still find a string that contains STOP, and you can then exit awk with an appropriate exit code to say that the tail command itself failed (or add a little extra processing to forward the precise exit code as parsed from the line that awk reads) – ricky116 Dec 31 '21 at 11:15