15

I need to the following things to make sure my application server is

  1. Tail a log file for a specific string
  2. Remain blocked until that string is printed
  3. However if the string is not printed for about 20 mins quit and throw and exception message like "Server took more that 20 mins to be up"
  4. If string is printed in the log file quit the loop and proceed.

Is there a way to include time outs in a while loop ?

pup784
  • 533
  • 3
  • 5
  • 15

5 Answers5

13
#!/bin/bash
tail -f logfile | grep 'certain_word' | read -t 1200 dummy_var
[ $? -eq 0 ]  && echo 'ok'  || echo 'server not up'

This reads anything written to logfile, searches for certain_word, echos ok if all is good, otherwise after waiting 1200 seconds (20 minutes) it complains.

jim mcnamara
  • 16,005
  • 2
  • 34
  • 51
  • Jim, can you explain what is 'read -t 1200 dummy_var' doing here ? – pup784 Dec 21 '12 at 05:42
  • @Kevin read -t is for later versions of bash. This works fine on my 4.1.0 version. If you try "help read" from the bash command line it will give the options your version of bash supports. – jim mcnamara Dec 21 '12 at 22:57
  • 1
    I did "help read " and it does support the timeout option -t timeout time out and return failure if a complete line of input is not read withint TIMEOUT seconds. The value of the TMOUT variable is the default timeout. TIMEOUT may be a fractional number. If TIMEOUT is 0, read returns success only if input is available on the specified file descriptor. The exit status is greater than 128 if the timeout is exceeded. Also my bash version is 4.2.24(1)-release (x86_64-pc-linux-gnu) – pup784 Dec 21 '12 at 23:21
  • Then I don't know why the debugger has a problem. How are you debugging? – jim mcnamara Dec 21 '12 at 23:24
  • sh -x timeout.sh . I tried debugging when I found that the script was just hanging rather that timing out. Just one question though, the 'dummy_var' is there because there has to be a variable name in the read command, it can be any variable name right ? – pup784 Dec 21 '12 at 23:29
  • Yes it can be any name, you do not need to do anything with it because grep decides if 'certain_word' shows up – jim mcnamara Dec 21 '12 at 23:30
  • sh -x will work correctly if the command which sh ultimately points to bash, on some system /bin/sh can be a link to something else, try set -x instead. – jim mcnamara Dec 21 '12 at 23:34
  • 1
    The read never returns for me. Tried it under OSX and Linux, Bash versions 3.2.48 & 3.2.25 respectively. – Joe Casadonte Aug 23 '14 at 16:44
  • @jimmcnamara Can you explain whats this line is doing read -t 1200 dummy_var [ $? -eq 0 ] – user Jan 20 '15 at 16:54
  • This doesn't work because although `read` will correctly timeout, tail & grep will keep on running because they'll get notified of the `read` exit only when they try to write output (which never happens until the match string arrives). – Gluck Aug 31 '17 at 07:38
1

You can use signal handlers from shell scripts (see http://www.ibm.com/developerworks/aix/library/au-usingtraps/index.html).

Basically, you'd define a function to be called on, say, signal 17, then put a sub-script in the background that will send that signal at some later time:

timeout(pid) {
   sleep 1200
   kill -SIGUSR1 $pid
}

watch_for_input() {
   tail -f file | grep item
}

trap 'echo "Not found"; exit' SIGUSR1
timeout($$) &
watch_for_input

Then if you reach 1200 seconds, your function is called and you can choose what to do (like signal your tail/grep combo that is watching for your pattern in order to kill it)

Zoe
  • 27,060
  • 21
  • 118
  • 148
Tony K.
  • 5,535
  • 23
  • 27
1

You can do it like this:

start_time=$(date +"%s")
while true
do
    elapsed_time=$(($(date +"%s") - $start_time))
    if [[ "$elapsed_time" -gt 1200 ]]; then
        break
    fi
    sleep 1
    if [[ $(grep -c "specific string" /path/to/log/file.log) -ge 1 ]]; then
        break
    fi
done
sampson-chen
  • 45,805
  • 12
  • 84
  • 81
0
time=0
found=0
while [ $time -lt 1200 ]; do
  out=$(tail logfile)
  if [[ $out =~ specificString ]]; then
    found=1
    break;
  fi  
  let time++
  sleep 1
done
echo $found
perreal
  • 94,503
  • 21
  • 155
  • 181
  • This won't work if the file is growing rapidly...you'll miss lines – Tony K. Dec 21 '12 at 02:50
  • 1
    This might introduce a sliding window problem because the instructions other than `sleep 1` also take up execution time. – sampson-chen Dec 21 '12 at 02:51
  • 1
    I don't think we are dealing with real-time accuracy here. And yes this might miss if logger output is fast but this can be fixed depending on requirements. – perreal Dec 21 '12 at 02:52
0

The accepted answer doesn't work and will never exit (because althouth read -t exits, the prior pipe commands (tail -f | grep) will only be notified of read -t exit when they try to write to output, which never happens until the string matches).

A one-liner is probably feasible, but here are scripted (working) approaches. Logic is the same for each one, they use kill to terminate the current script after the timeout. Perl is probably more widely available than gawk/read -t

#!/bin/bash

FILE="$1"
MATCH="$2"

# Uses read -t, kill after timeout
#tail -f "$FILE" | grep "$MATCH" | (read -t 1 a ; kill $$)

# Uses gawk read timeout ability (not available in awk)
#tail -f "$FILE" | grep "$MATCH" | gawk "BEGIN {PROCINFO[\"/dev/stdin\", \"READ_TIMEOUT\"] = 1000;getline < \"/dev/stdin\"; system(\"kill $$\")}"

# Uses perl & alarm signal
#tail -f "$FILE" | grep "$MATCH" | perl -e "\$SIG{ALRM} = sub { `kill $$`;exit; };alarm(1);<>;"
Gluck
  • 2,933
  • 16
  • 28