bashtail

Ending tail -f started in a shell script


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
}

Solution

  • 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
    }