Authors Top

If you have a few years of experience in the Linux ecosystem, and you’re interested in sharing that experience with the community, have a look at our Contribution Guidelines.

1. Overview

Sometimes, we need to rerun a command several times if the execution of the command isn’t successful. For example, we might be transferring files over the network, but the network resources may be temporarily unavailable. Therefore, we need a retry mechanism in those cases.

In this tutorial, we’ll discuss several methods for retrying an unsuccessful command.

2. Using the for Loop

If we want to rerun a command a specific number of times when it’s unsuccessful, the for loop is an ideal choice.

We’ll retry the ls command in our examples to keep the examples simple. We’ll check the existence of the file /tmp/file using ls. Hence, it’ll be easier to create a successful or an unsuccessful trial in the examples just by creating or deleting the file. A more realistic example might be pinging a machine or transferring data over the network.

Let’s inspect the usage of the for loop using the following script, for_retry.sh:

#!/bin/bash

max_iteration=5

for i in $(seq 1 $max_iteration)
do
  ls /tmp/file >& /dev/null
  result=$?
  if [[ $result -eq 0 ]]
  then
    echo "Result successful"
    break   
  else
    echo "Result unsuccessful"
    sleep 1
  fi
done

if [[ $result -ne 0 ]]
then
  echo "All of the trials failed!!!"
fi

First, we define a variable named max_iteration. It’s the maximum trial number.

Then, we create a list with length max_iteration using the seq command.

We start to run the command ls /tmp/file >& /dev/null for each value in the list. We save the exit status of ls in the result variable using result=$?.

If the result is successful, i.e., the exit status is 0, we print the message Result successful and exit from the for loop using the break command. Otherwise, we print the message Result unsuccessful, and after sleeping for one second, we continue iterating within the loop. We use sleep 1 because running the command aggressively without sleeping may increase the CPU consumption of the script.

Finally, we print the message All of the trials failed!!! in case all trials are unsuccessful.

Let’s run the script:

$ for_retry.sh
Result unsuccessful
Result unsuccessful
Result unsuccessful
Result unsuccessful
Result unsuccessful
All of the trials failed!!!

As /tmp/file didn’t exist, all of the trials were unsuccessful.

Now, let’s run the script once more after creating /tmp/file using the touch command:

$ touch /tmp/file
$ for_retry.sh
Result successful

Therefore, we could use the for loop for retrying an unsuccessful command.

3. Using the while Loop

The while loop is another option for rerunning a command in case of a failure.

3.1. Infinite Loop Case

The first script we’ll use is while_infinite.sh:

#!/bin/bash

while ls /tmp/file >& /dev/null; [[ $? -ne 0 ]];
do
  echo "Result unsuccessful"
  sleep 1
done

echo "Result successful"

We check the existence of the file using ls /tmp/file >& /dev/null in the condition part of while. The second command in the condition part of while checks the exit status of ls using [[ $? -ne 0 ]]. It’s possible to use more than one command in the condition part of while.

If the exit status of ls isn’t equal to 0, i.e., the file doesn’t exist, then the condition of the while loop is true. Therefore, echo “Result unsuccessful” statement within the while loop is executed. The script sleeps for a second using sleep 1, and then we check the existence of the file in the condition part of while again.

If the condition is false, i.e., the file exists, we exit out of the while loop. Finally, the echo “Result successful” command after we exit from the loop.

Let’s run while_infinite.sh:

$ while_infinite.sh
Result unsuccessful
Result unsuccessful
Result unsuccessful
...

The file /tmp/file doesn’t exist, so we keep on rerunning ls within the while loop. Let’s create the file with touch in another terminal:

$ touch /tmp/file

Now, let’s check the output of while_infinite.sh:

$ while_infinite.sh
Result unsuccessful
Result unsuccessful
Result unsuccessful
Result unsuccessful
Result unsuccessful
Result successful

As soon as we created the file, while_infinite.sh exited printing Result successful, as expected.

3.2. Finite Case

while_infinite.sh will run forever if the file isn’t created. Therefore, it might be a good idea to try for a limited number of times and then give up. Let’s analyze the following script, while_finite.sh:

#!/bin/bash

max_iteration=5
iteration=1
while ls /tmp/file >& /dev/null; [[ $? -ne 0 ]];
do
  echo "Result unsuccessful"

  if [[ $iteration -eq $max_iteration ]]
  then
    break
  fi

  sleep 1
  ((iteration++))  
done

if [[ $iteration -eq $max_iteration ]]
then
  echo "All of the $max_iteration trials failed!!!"
else
  echo "Result successful"
fi

The script while_finite.sh looks like while_infinite.sh, but there are some differences. Let’s discuss them.

First, there are two variables, max_iteration and iteration. max_iteration corresponds to the maximum trial number. On the other hand, iteration corresponds to how many times we tried the command. We increase the value of iteration in each iteration using the statement ((iteration++)).

Second, if the number of trials becomes equal to the maximum trial number, then the condition of the if statement within the while loop ([[ $iteration -eq $max_iteration ]]) becomes true, and we exit from the loop using break.

Finally, we print whether we’re successful or not by comparing max_iteration and iteration.

Let’s first delete /tmp/file, and run while_finite.sh:

$ rm -f /tmp/file
$ while_finite.sh
Result unsuccessful
Result unsuccessful
Result unsuccessful
Result unsuccessful
Result unsuccessful
All of the 5 trials failed!!!

We tried five times and finally gave up by printing the message All of the 5 trials failed!!!, as expected.

Let’s try it once more:

$ rm -f /tmp/file
$ while_finite.sh
Result unsuccessful
...

Now, while_finite.sh is running. Let’s again create the file /tmp/file in another terminal:

$ touch /tmp/file

Let’s check the output of while_finite.sh:

$ while_finite.sh
Result unsuccessful
Result unsuccessful
Result successful

As soon as we created the file, we exited from the while loop, as expected. Therefore, we can use the while loop to execute an unsuccessful command again.

4. Using the until Loop

We can also use the until loop for retrying a command. It’s similar to the while loop.

4.1. Infinite Loop Case

Let’s examine it using the following script, until_infinite.sh:

#!/bin/bash

until ls /tmp/file >& /dev/null; [[ $? -eq 0 ]];
do
  echo "Result unsuccessful"
  sleep 1
done

echo "Result successful"

The only difference between until_infinite.sh from while_infinite.sh is the part that checks the exit status of ls in the condition part of the if statement. Now, we check whether the exit status is 0, i.e., we try until the execution of ls is successful.

Let’s run it:

$ rm -f /tmp/file
$ until_infinite.sh
Result unsuccessful
Result unsuccessful
Result unsuccessful
...

Since /tmp/file doesn’t exist, the script keeps running. Let’s create the file using touch in another terminal:

$ touch /tmp/file

Let’s check the output of until_infinite.sh:

$ rm -f /tmp/file
$ until_infinite.sh
Result unsuccessful
Result unsuccessful
Result unsuccessful
Result unsuccessful
Result unsuccessful
Result successful

As soon as we created /tmp/file, the script exited immediately, as expected.

4.2. Finite Case

We can also retry the command a finite number of times using the until loop. We’ll use the following script, until_finite.sh:

#!/bin/bash

max_iteration=5
iteration=1
until ls /tmp/file >& /dev/null; [[ $? -eq 0 ]];
do
  echo "Result unsuccessful"

  if [[ $iteration -eq $max_iteration ]]
  then
    break
  fi

  sleep 1
  ((iteration++))  
done

if [[ $iteration -eq $max_iteration ]]
then
  echo "All of the $max_iteration trials failed!!!"
else
  echo "Result successful"
fi

This script is nearly the same as while_finite.sh with a single exception. We just replaced the while statement with the until statement. Therefore, it’ll work in the same manner.

5. Conclusion

In this article, we discussed several methods for rerunning a command if it returns an error.

Firstly, we examined the usage with the for loop. It’s useful for retrying the failed command a specific number of times.

Secondly, we discussed the while loop. We could use the while loop for both a finite and infinite number of trials.

Finally, we examined the until loop. We saw that its usage is similar to the while loop.

Authors Bottom

If you have a few years of experience in the Linux ecosystem, and you’re interested in sharing that experience with the community, have a look at our Contribution Guidelines.

Comments are closed on this article!