0

I know this question has been asked a lot of time, but trust me i have tried many suggestions. What i am trying to do is continiously monitor the logfile of a tomcat server to detect its startup. Its based on this stack post

I have modified it a little bit to add a timeout so that it won't wait for ever if the string is not present at all in the file. The code looks like below.

echo 0 > flag
timeout  $DEPLOY_WAIT tail -n0 -F $TOMCAT_LOG | while read LOGLINE
do
  [[ "${LOGLINE}" == *"INFO: Server startup in"* ]]  && echo 1 > flag && pkill -P $$ timeout
done
x=$(cat flag)
if [ $x -ne 0 ];then

Here you can see i am writing to a flag file to detect whether the tail timed out out or if it found a matching string in the file. If i change it to a variable like

flag=0
timeout  $DEPLOY_WAIT tail -n0 -F $TOMCAT_LOG | while read LOGLINE
do
  [[ "${LOGLINE}" == *"INFO: Server startup in"* ]]  && flag=1 && pkill -P $$ timeout
done
if [ $flag -ne 0];then

then the variable flag always have the value 0. The update done in the while loop is lost. I did some research and found out that this is because the pipe is causing bash to spawn a new sub shell and parameter are passed from parent to child and not vice versa. A solution suggested by this post was to rewrite the thing without using pipes like

flag=0
while read LOGLINE;
do
  [[ "${LOGLINE}" == *"INFO: Server startup in"* ]]  && flag=1 && pkill -P $$ timeout
done <<< $(timeout  $DEPLOY_WAIT tail -n0 -F $TOMCAT_LOG)
if [ $flag -ne 0];then

The problem with this solution is that the data is give to the while loop only after the command timeout completes[and the code to detect the timeout process and kill it also changes. But doesn't matter, so not included] and i am not able to detect the string as soon as it arrives in the file. The script waits for the period of the timeout, no matter what. I am looking for way to combine the two into a single elegant solution not using files as flag.

varun
  • 341
  • 2
  • 17
  • You are making things hard on yourself. Search for `inotifywait` on StackOverflow. You can use it to monitor your log for changes and do whatever you need when your log changes. (pay attention to the `-m` and `-e` options) – David C. Rankin Aug 10 '18 at 06:09

2 Answers2

1

Use PIPESTATUS to check, if the timeout has been reached. In case of an timeout, the return value is 124.

$ timeout 1 sleep 2 | read
Terminated
$ echo $PIPESTATUS 
124
$ timeout 2 sleep 1 | read
$ echo $PIPESTATUS 
0

And do not kill timeout. It terminates itself, if the child dies.

ceving
  • 21,900
  • 13
  • 104
  • 178
0

The problem with the solution you tried:

done <<< $(timeout  $DEPLOY_WAIT tail -n0 -F $TOMCAT_LOG)

is that you're capturing the entire output of the timeout ... command with $( ) as a string (which means that it must wait until the process exits), and then passing that to the loop as a here-string (with <<<). In order for the loop to process output as it's produced, you need to mimic a pipe, like this:

done < <(timeout  $DEPLOY_WAIT tail -n0 -F $TOMCAT_LOG)

This looks similar, but it's quite different in the details. The <( ) part is called process substitution -- it runs the timeout ... command as a subprocess, and effectively inserts a the filename of a named pipe connected to that process's stdout. Then the resulting < pipename part makes the loop read from that named pipe. The effect is pretty much the same as a regular pipe (as in your first attempt), except that the loop isn't forced into a subshell.

Gordon Davisson
  • 118,432
  • 16
  • 123
  • 151
  • this worked for me but had t modify the script a little bit to find and kill the timeout process. Earlier the timeout process had the shell script process itself has its parent. After this change a new process is created similar to the original shell scirpt and the timeout is running as a child of this process. so had to use the below comamnd to detect it. depoy.sh is the script name. pkill -P \`pgrep -P $$ deploy\` timeout – varun Aug 10 '18 at 09:53