Retrying a bash command
There are many reasons for commands to fail. Some of them provide retry options, but others don't. In this post, I describe how to retry a bash command multiple times until it succeeds.
To retry a command in Bash, you can use a while
loop. The loop will execute the command and if the command returns a non-zero exit code indicating an error, the loop will try again. If the command succeeds, the loop will exit.
# Set the maximum number of attempts
max_attempts=5
# Set a counter for the number of attempts
attempt_num=1
# Set a flag to indicate whether the command was successful
success=false
# Loop until the command is successful or the maximum number of attempts is reached
while [ $success = false ] && [ $attempt_num -le $max_attempts ]; do
# Execute the command
echo "TODO Replace this line with the actual command to execute"
# Check the exit code of the command
if [ $? -eq 0 ]; then
# The command was successful
success=true
else
# The command was not successful
echo "Attempt $attempt_num failed. Trying again..."
# Increment the attempt counter
attempt_num=$(( attempt_num + 1 ))
fi
done
# Check if the command was successful
if [ $success = true ]; then
# The command was successful
echo "The command was successful after $attempt_num attempts."
else
# The command was not successful
echo "The command failed after $max_attempts attempts."
fi
Of course, you don't want to do it for every command. So, you can create a function. The function will take the command as an argument and execute it.
retry() {
local retries="$1" # First argument
local command="$2" # Second argument
# Run the command, and save the exit code
$command
local exit_code=$?
# If the exit code is non-zero (i.e. command failed), and we have not
# reached the maximum number of retries, run the command again
if [[ $exit_code -ne 0 && $retries -gt 0 ]]; then
retry $(($retries - 1)) "$command"
else
# Return the exit code from the command
return $exit_code
fi
}
# example usage:
retry 5 "ls /dummy"
Maybe you want to introduce a delay between retries. You can add a sleep
command to the function.
# Define the retry function
wait_and_retry() {
local retries="$1"
local wait="$2"
local command="$3"
# Run the command, and save the exit code
$command
local exit_code=$?
# If the exit code is non-zero (i.e. command failed), and we have not
# reached the maximum number of retries, run the command again
if [[ $exit_code -ne 0 && $retries -gt 0 ]]; then
# Wait before retrying
sleep $wait
wait_and_retry $(($retries - 1)) $wait "$command"
else
# Return the exit code from the command
return $exit_code
fi
}
# example usage:
wait_and_retry 5 1s "ls /dummy"
Note that the previous method will not work if the command is a pipeline. For example, the following command will not work:
# Does not work
retry 5 "echo 'TODO' | grep 'TODO'"
Also, if you use set -e
, which instructs bash
to exit if any command in the pipeline fails, the retry function will not work as it will exit before you get a chance to retry it.
# Define the retry function
retry() {
local retries="$1"
local command="$2"
local options="$-" # Get the current "set" options
# Disable set -e
if [[ $options == *e* ]]; then
set +e
fi
# Run the command, and save the exit code
$command
local exit_code=$?
# restore initial options
if [[ $options == *e* ]]; then
set -e
fi
# If the exit code is non-zero (i.e. command failed), and we have not
# reached the maximum number of retries, run the command again
if [[ $exit_code -ne 0 && $retries -gt 0 ]]; then
retry $(($retries - 1)) "$command"
else
# Return the exit code from the command
return $exit_code
fi
}
# example usage:
set -e
retry 5 "ls /dummy"
Do you have a question or a suggestion about this post? Contact me!