0

In have a function which I call in a loop in parallel:

#!/bin/bash

my_func()
{
    until [[ $entity status is OK ]]; do
        sleep 5
        echo "count=$count"
        if (( timeout_flag == 1 )); then
            break
        fi
    done
}

_arr=(e1 e2 e3)
count=0
timeout=60
timeout_flag=0

for entity in "${_arr[@]}"; do
    my_func "${entity}" &
done

while [[ "${count}" -lt "${timeout}" ]]; do
    sleep 5
    count=count+5
done
timeout_flag=1
echo "Timeout reached"
sleep 1

I want all functions to periodically check until the status of an entity is OK and wait for all OK statuses or stop all the (remaining) functions when timeout is reached? Whatever comes first. The above does not seem to work and I need to kill it manually.

  • Why am I getting "Timeout reached" echoed at the end and the script is not waiting for functions to finish?

  • Why count=0 in my_func and is not increasing?

lubik
  • 79
  • 3
  • 9
  • 2
    Copy and paste your script to https://shellcheck.net to see your mistakes, and read about subshells to see why your logic doesn't work. – oguz ismail May 01 '20 at 13:29
  • What is the point of the sleep loop? It does not check anything, just (when fixed) increment a variable and sleep again... Also, check out the [timeout](https://ss64.com/bash/timeout.html) command? – Keldorn May 01 '20 at 14:00
  • @Keldorn I wanted to increment the counter so once the timeout is reached, I could set the flag for running functions – lubik May 01 '20 at 14:14
  • 1
    Note that `count=count+5` is not very useful; it assigns the string `"count+5"` to the variable `count`. You probably had in mind `count=$(($count + 5))` or variants on the theme, such as `((count += 5))`. – Jonathan Leffler May 01 '20 at 15:02

1 Answers1

2

If you want to timeout each function after 60 seconds, you can use the timeout command; it is made precisely for that:

#!/bin/bash                                                                                                                                                                                   

my_func() {
    to_sleep=$1
    stdbuf -oL echo "Request to sleep at least $to_sleep s"
    slept=0
    while [[ $slept -lt $to_sleep ]]; do
        sleep 1
        slept=$((slept + 1))
    done
    echo "Successfully slept $slept s ($to_sleep s requested)"
}

export -f my_func

_arr=(1 3 5)
maxtime=4s

for entity in "${_arr[@]}"; do
    stdbuf -oL echo "Launching with $entity s"
    timeout $maxtime bash -c "my_func $entity" &
done

The output is

Launching with 1 s
Launching with 3 s
Launching with 5 s
Request to sleep at least 1 s
Request to sleep at least 3 s
Request to sleep at least 5 s
Successfully slept 1 s (1 s requested)
Successfully slept 3 s (3 s requested)

Note that the "5 s" case did not succeed.

Notes:

  • stdbuf -oL is here to flush the echo output directly
  • export -f my_func and timeout $maxtime bash -c are added to make your functon visible in the new shell started by timeout, following this answer.
Keldorn
  • 1,980
  • 15
  • 25
  • I am getting `timeout: failed to run command ‘my_func’: No such file or directory` – lubik May 01 '20 at 14:27
  • @lubik `my_func` is the function you want to call. Replace with what you want. – Keldorn May 01 '20 at 14:30
  • that's what I am doing, `my_func` is the name of my function – lubik May 01 '20 at 14:38
  • @lubik Oops, the function was not available. I updated my answer using [this answer](https://stackoverflow.com/a/31311722/6352677). – Keldorn May 01 '20 at 15:20
  • Thank you, works like a charm. I had another typo - had '&' at the end of timeout command inside double quotes. However, I need to hit Enter in terminal to return to main shell because sub-shells are displaying their outputs until they are timed out. Would this work automatically? For example in Jenkins. – lubik May 01 '20 at 16:02