Retrying a bash command

 
 
  • Gérald Barré

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.

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

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.

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"

Maybe you want to introduce a delay between retries. You can add a sleep command 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 the previous method will not work if the command is a pipeline. For example, the following command will not work:

Shell
# 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.

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!

Follow me:
Enjoy this blog?Buy Me A Coffee💖 Sponsor on GitHub