Retrying a bash command

 
 
  • Gérald Barré

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!

Follow me:
Enjoy this blog?