Search code examples
linuxbashshellprocessfork

How to get correct tail PID under multiple process in shell


I am implementing monitor_log function which will tail the most recent line from running log and check required string with while loop, the timeout logic should be when the tail log running over 300 seconds, it must close the tail and while loop pipeline. Refer to How to timeout a tail pipeline properly on shell, as mentioned even monitor_log function finished, it will left tail process on background, so I have to manually kill it in case of hundreds times calling monitor_log will left hundreds of tail process.

The function monitor_log below kill correct tail PID only under single process situation, because tail_pid=$(ps -ef | grep 'tail' | cut -d' ' -f5) will return the correct tail PID which on the only process. When comes to multiple process situation(process fork), for example, multiple calls of monitor_log function to parallel tail log, there will be multiple tail process running, tail_pid=$(ps -ef | grep 'tail' | cut -d' ' -f5) not able to return exactly tail PID which belong to current fork.

So is that possible to NOT left tail process when monitor_log function finished ? OR if left tail process, is there a proper way to find its correct PID (not the tail PID from other thread) and kill it under multiple process situation ?

function monitor_log() {
  if [[ -f "running.log" ]]; then
    # Tail the running log last line and keep check required string
    while read -t 300 tail_line
    do
      if [[ "$tail_line" == "required string" ]]; then
        capture_flag=1
      fi
      if [[ $capture_flag -eq 1 ]]; then
        break;
      fi
    done < <(tail -n 1 -f "running.log")
    # Not get correct tail PID under multiple process
    tail_pid=$(ps -ef | grep 'tail' | cut -d' ' -f5)
    kill -13 $tail_pid
  fi
}

Solution

  • You may try below code:

    function monitor_log() {
      if [[ -f "running.log" ]]; then
        # Tail the running log last line and keep check required string
        while read -t 300 tail_line
        do
          if [[ "$tail_line" == "required string" ]]; then
            capture_flag=1
          fi
          if [[ $capture_flag -eq 1 ]]; then
            break;
          fi
        done < <(tail -n 1 -f "running.log")
    
         tailpid=$!
    
         kill $tailpid
      fi
    }
    

    > $! Gives the Process ID (PID) of last job run in background

    Update: This would kill the subshell which triggers the tail as suggested in comment

    Another way might be add timeout to the tail command as below:

    timeout 300s tail -n 1 -f "running.log"