Home > other >  Stop a loop process in shell scripting after some time
Stop a loop process in shell scripting after some time

Time:03-09

I have a script that looks like this, this script is checking whether my pods is in Running state or Not by redefining x on every loop.

x=$(/opt/oc get pods --selector app=${bamboo.shortPlanName} -o jsonpath='{range .items[]}{.status.phase}{"\n"}{end}')

until [ "$x" == "Running" ];
do
   sleep 5
   x=$(/opt/oc get pods --selector app=${bamboo.shortPlanName} -o jsonpath='{range .items[]}{.status.phase}{"\n"}{end}')
   echo $x
done

But, I want to modify my current script to also support timeout, I mean after 60 second of looping, then it should be stop, or after 12 times of looping, then it should be stop. Any idea how to do that?

CodePudding user response:

bash provides a special variable SECONDS that can be used as a rough timer.

SECONDS=0
while (( SECONDS < 60)); do
    x=$(/opt/oc get pods --selector app=${bamboo.shortPlanName} -o jsonpath='{range .items[]}{.status.phase}{"\n"}{end}')
    if [[ $x == Running ]]; then
        break
    fi
    sleep 5
done

The expansion of SECONDS gives you not the assigned value, but the difference between the numbers of seconds since the assignment and the assigned value. The effect is like a variable whose value is incremented by 1 each second.

CodePudding user response:

I think trap is the easiest way to have an accurate timeout.

Prototype:

#! /bin/bash

HALT=0
TIMEOUT=4

# Trap for SIGALRM
stop_loop() {
    HALT=1
}

# Set trap
trap stop_loop SIGALRM

# The timeout goes after $TIMEOUT seconds.
{ sleep $TIMEOUT && kill -SIGALRM $$ & }

# Main loop
until false || [[ $HALT -eq 1 ]]; do 
    sleep 1
    echo 'loop'
done

echo 'out of loop'
exit 0

In your case, this looks a something like:

HALT=0
TIMEOUT=4

stop_loop() {
    HALT=1
}

trap stop_loop SIGALRM

x=$(/opt/oc get pods --selector app=${bamboo.shortPlanName} \
    -o jsonpath='{range .items[]}{.status.phase}{"\n"}{end}')

{ sleep $TIMEOUT && kill -SIGALRM $$ & }

until [ "$x" == "Running" ] || [[ $HALT -eq 1 ]];
do
   sleep 5
   x=$(/opt/oc get pods --selector app=${bamboo.shortPlanName} \
        -o jsonpath='{range .items[]}{.status.phase}{"\n"}{end}')
   echo $x
done
exit 0

CodePudding user response:

For timeout after 60 seconds try this Shellcheck-clean code:

#! /bin/bash -p

readonly kOC_SLEEP_SECS=5
readonly kOC_TIMEOUT_SECS=60

initial_secs=$SECONDS
while
    status=$(/opt/oc get pods --selector app=bamboo.shortPlanName       \
                -o jsonpath='{range .items[]}{.status.phase}{"\n"}{end}')
    printf '%s\n' "$status"
    [[ $status != Running ]]
do
    if (( (SECONDS - initial_secs) >= kOC_TIMEOUT_SECS )); then
        echo 'ERROR: Timed out' >&2
        exit 1
    fi
    sleep -- "$kOC_SLEEP_SECS"
done
  • I replaced app=${bamboo.shortPlanName} with app=bamboo.shortPlanName because the old code was causing Bash errors. You'll need to fix it properly.
  • See Why is printf better than echo? for an explanation of why I replaced echo with printf for printing the status.
  • The code treats a timeout as an error, and exits with bad status. You might want to do something different.
  • The actual timeout period will be somewhere between 60 and 65 (or maybe a little more) seconds. You'll need to do something different if you need a more accurate timeout.

For a timeout after 12 iterations try this Shellcheck-clean variation on the code above:

#! /bin/bash -p

readonly kOC_SLEEP_SECS=5
readonly kOC_MAX_ITERS=12

oc_iters=0
while
    status=$(/opt/oc get pods --selector app=bamboo.shortPlanName           \
                -o jsonpath='{range .items[]}{.status.phase}{"\n"}{end}')
    printf '%s\n' "$status"
    [[ $status != Running ]]
do
   if ((   oc_iters >= kOC_MAX_ITERS )); then
       echo 'ERROR: Timed out' >&2
       exit 1
    fi
   sleep -- "$kOC_SLEEP_SECS"
done
  • Related