Commands can fail for many reasons. Some have built-in retry options, but many don't. Here are a few ways to retry a bash command multiple times until it succeeds.
To retry a command in Bash, you can use a while loop. If the command returns a non-zero exit code, the loop retries. Once the command succeeds, the loop exits.
Shell
# 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
Writing this inline every time is tedious. Instead, you can extract it into a reusable function that takes the command as an argument.
Shell
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"
To introduce a delay between retries, add a sleep call to the function.
Shell
# 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 this approach does not work with pipeline commands. For example, the following will not work:
Shell
# Does not work
retry 5 "echo 'TODO' | grep 'TODO'"
If you use set -e, which causes Bash to exit immediately when any command fails, the retry function will not work because it exits before you can retry. The following version handles this by temporarily disabling set -e inside the function.
Shell
# 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!