sorin
sorin

Reputation: 170440

Retry a sequence of commands in bash for a limited number of times

I am looking for a nice and clean method for implementing a retry loop-mechanics in bash.

Here is how is supposed to work:

retries = 3
while retries:
    cmd1
    cmd2
    cmd3
    if not_error:
        break
    echo "something went wrong, let's wait 60 seconds and retry"
    sleep 60
    retries --
if retries = 0:
    exit "Failed!"

Each of the commands can fail and I would prefer to stop running the next commands and just do to the retry code.

Upvotes: 7

Views: 10496

Answers (7)

link89
link89

Reputation: 1763

My solution is to use for loop, bash and Here document.

#!/bin/bash
set -e

for count in $(seq 1 5); do bash <<EOF && break
echo "try $count ..."
false
EOF
done && echo ok || echo err

It's easy to use the same method to run command remotely or inside container, for example,

#!/bin/bash
set -e

for count in $(seq 1 5); do ssh remote-host bash <<EOF && break
echo "try $count ..."
false
EOF
done && echo ok || echo err

Upvotes: 0

user3450160
user3450160

Reputation: 1

This is what worked when using a plain /bin/sh shell (where bash is not available):

retries=3
while [[ $retries -gt 0 ]]; do 
    if cmd1 && cmd2 && cmd3; then
        echo "Passed!"
        exit 0 
    else
        echo "something went wrong, let's wait 2 seconds and retry"
        sleep 2
        retries=$((retries - 1))
    fi
done
echo "Failed!"
exit 1

Upvotes: 0

anubhava
anubhava

Reputation: 785128

Something like this:

#!/bin/bash

retries=3

for ((i=0; i<retries; i++)); do
    cmd1 && cmd2 && cmd3
    [[ $? -eq 0 ]] && break

    echo "something went wrong, let's wait 60 seconds and retry"
    sleep 60
done

(( retries == i )) && { echo 'Failed!'; exit 1; }
exit 0

Upvotes: 5

nkadwa
nkadwa

Reputation: 859

This is behavior that is worth externalizing and reusing:

See: https://github.com/kadwanev/retry

retry -t 3 pipeline.sh
if [ $? -ne 0 ]; then
  echo "Failed"
  exit 1
fi

This will retry up to 3 times and then return if every attempt failed.

Upvotes: 2

Laser
Laser

Reputation: 6960

You can try this solution:

retries=3
while [ retries gt 0 ]; do 
    if cmd1 && cmd2 && cmd3; then
        echo "Passed!"
        exit 0 
    else
    echo "something went wrong, let's wait 60 seconds and retry"
    sleep 60
    ((retries --))
    fi
done
    echo "Failed!"
    exit 1

Upvotes: 0

secolive
secolive

Reputation: 589

Over the years I have come with the following "core" loop for such situations:

(r=3;while ! some_cmd ; do ((--r))||exit;sleep 60;done)

This has the benefit that the logic is self-contained into a single statement, and the expression will evaluate to true if the command succeeded, or false if it did not after 3 attempts. This is quite useful if you're running with set -e, or to replace an existing simple command with multiple retries of that command.

In your case this could translate to:

if ! (r=3; while ! { cmd1 && cmd2 && cm3 ; } ; do
          ((--r)||exit
          echo "something went wrong, let's wait 60 seconds and retry"
          sleep 60;done) ; then
    echo "Failed!"
    exit 1
fi

echo "Passed!"
exit 0

Upvotes: 14

chepner
chepner

Reputation: 531125

This may not be the cleanest if the individual commands are long:

retries=3
while ((retries > 0)); do
    cmd1 &&
    cmd2 &&
    cmd3 && break

    echo "something went wrong, let's wait 60 seconds and retry"
    sleep 60
    ((retries --))
done
if ((retries == 0 )); then
    echo "Failed!"
    exit 1
fi

The && operator runs the command on its right if the command on its left succeeds. If all three succeed, the break exits the loop.

Upvotes: 3

Related Questions