Ending tail -f started in a shell script

rangalo picture rangalo · Jan 11, 2010 · Viewed 40k times · Source

I have the following.

  1. A Java process writing logs to the stdout
  2. A shell script starting the Java process
  3. Another shell script which executes the previous one and redirects the log
  4. I check the log file with the tail -f command for the success message.

Even if I have exit 0 in the code I cannot end the tail -f process.

Which doesn't let my script to finish. Is there any other way of doing this in Bash?

The code looks like the following.

function startServer() {
  touch logfile
  startJavaprocess > logfile &

  tail -f logfile | while read line 
  do
    if echo $line | grep -q 'Started'; then
      echo 'Server Started'
      exit 0
    fi
  done
}

Answer

falstro picture falstro · Jan 11, 2010

The best answer I can come up with is this

  1. Put a timeout on the read, tail -f logfile | read -t 30 line
  2. Start tail with --pid=$$, that way it'll exit when the bash-process has finished.

It'll cover all cases I can think of (server hangs with no output, server exits, server starts correctly).

Dont forget to start your tail before the server.

tail -n0 -F logfile 2>/dev/null | while read -t 30 line

the -F will 'read' the file even if it doesn't exist (start reading it when it appears). The -n0 won't read anything already in the file, so you can keep appending to the logfile instead of overwriting it each time, and to standard log rotation on it.

EDIT:
Ok, so a rather crude 'solution', if you're using tail. There are probably better solutions using something else but tail, but I got to give it to you, tail gets you out of the broken-pipe quite nicely. A 'tee' which is able to handle SIGPIPE would probably work better. The java process actively doing a file system drop with an 'im alive' message of some sort is probably even easier to wait for.

function startServer() {
  touch logfile

  # 30 second timeout.
  sleep 30 &
  timerPid=$!

  tail -n0 -F --pid=$timerPid logfile | while read line 
  do
    if echo $line | grep -q 'Started'; then
      echo 'Server Started'
      # stop the timer..
      kill $timerPid
    fi
  done &

  startJavaprocess > logfile &

  # wait for the timer to expire (or be killed)
  wait %sleep
}