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.