tail -f and then exit on matching string

5,948

Solution 1

Something like this?

mkfifo child.fifo
tail -f child.out > child.fifo &
pid=$!
grep -q B child.fifo && kill $pid

in full:

#!/bin/sh    
rm -f child.out
./child.sh > child.out &
mkfifo child.fifo
tail -f child.out > child.fifo &
pid=$!
grep -q B child.fifo && kill $pid
rm child.fifo

Seems to run in 20 seconds.

$ time ./test2.sh

real    0m20.156s
user    0m0.033s
sys     0m0.058s

UPDATE

This way seems to work too:

#!/bin/sh
rm -f child.out
./child.sh > child.out &
(tail -f child.out | grep -q B child.out)

And if you're seeing it exit immediately sometimes, try adding a sleep 1, i.e.

#!/bin/sh
rm -f child.out
./child.sh > child.out &
sleep 1
(tail -f child.out | grep -q B child.out)

Solution 2

All on one line, replace 'do foo' with command'

# service tomcat6 start
# tail -f /var/log/tomcat6/catalina.out | awk '/Server startup/{system("do foo"); exit 0}'

Solution 3

Lose the tail. Since you start with an empty child.out, you do not need it and (as you have found) it complicates matters. Just grep for the first instance in a sleep loop.

until fgrep -q "Server startup" child.out; do sleep 1; done

If you do need to keep the tail because you do not always start with an empty catalina.out, place the tail inside the loop:

until tail child.out| fgrep -q "Server startup"; do sleep 1; done
Share:
5,948

Related videos on Youtube

martin
Author by

martin

Updated on September 17, 2022

Comments

  • martin
    martin almost 2 years

    I am trying to configure a startup script which will startup tomcat, monitor the catalina.out for the string "Server startup", and then run another process. I have been trying various combinations of tail -f with grep and awk, but haven't got anything working yet. The main issue I am having seems to be with forcing the tail to die after grep or awk have matched the string.

    I have simplified to the following test case.

    test.sh is listed below:
    
    #!/bin/sh    
    rm -f child.out
    ./child.sh > child.out &
    tail -f child.out | grep -q B
    
    child.sh is listed below:
    
    #!/bin/sh
    echo A
    sleep 20
    echo B
    echo C
    sleep 40
    echo D
    

    The behavior I am seeing is that grep exits after 20 seconds , however the tail will take a further 40 seconds to die. I understand why this is happening - tail will only notice that the pipe is gone when it writes to it which only happens when data gets appended to the file. This is compounded by the fact that tail is to be buffering the data and outputting the B and C characters as a single write (I confirmed this by strace). I have attempted to fix that with solutions I found elsewhere, such as using unbuffer command, but that didn't help.

    Anybody got any ideas for how to get this working how I expect it? Or ideas for waiting for successful Tomcat start (thinking about waiting for a TCP port to know it has started, but suspect that will become more complex that what I am trying to do now). I have managed to get it working with awk doing a "killall tail" on match, but I am not happy with that solution. Note I am trying to get this to work on RHEL4.

  • martin
    martin over 13 years
    That is a pretty cool idea, however just trying it and it returns back straight away rather than after the expected 20 seconds. This seems to be the due to the behaviour of grep on the fifo file. Maybe I need to try this method with awk. I will update if I get it working.
  • Mikel
    Mikel over 13 years
    Perhaps you saw an earlier draft I was editing. The above version takes 20 seconds, as shown by time ./test2.sh.
  • martin
    martin over 13 years
    Working fine now, thanks for your help. Ideally I would like in a single piped command, but I don't think that is possible, and the solution is not overly complex. Can't believe now I spent most of yesterday trying to get that working!
  • Mikel
    Mikel over 13 years
    No problem! Glad to help.
  • poige
    poige about 12 years
    @Patrick, I see no sense in piping through grep -q B filename, since it doesn't check its STDIN, just file's contents (as expected, though).