Bash – How to get this script to error exit based on result of for loop

bashshell-script

I have a bash script which uses set -o errexit so that on error the entire script exits at the point of failure.
The script runs a curl command which sometimes fails to retrieve the intended file – however when this occurs the script doesn't error exit.

I have added a for loop to

  1. pause for a few seconds then retry the curl command
  2. use false at the bottom of the for loop to define a default non-zero exit status – if the curl command succeeds – the loop breaks and the exit status of the last command should be zero.
#! /bin/bash

set -o errexit

# ...

for (( i=1; i<5; i++ ))
do
    echo "attempt number: "$i
    curl -LSso ~/.vim/autoload/pathogen.vim https://tpo.pe/pathogen.vim
    if [ -f ~/.vim/autoload/pathogen.vim ]
    then
        echo "file has been retrieved by curl, so breaking now..."
        break;
    fi

    echo "curl'ed file doesn't yet exist, so now will wait 5 seconds and retry"
    sleep 5
    # exit with non-zero status so main script will errexit
    false

done

# rest of script .....

The problem is when the curl command fails, the loop retries the command five times – if all attempts are unsuccessful the for loop finishes and the main script resumes – instead of triggering the errexit.
How can I get the entire script to exit if this curl statement fails?

Best Answer

Replace:

done

with:

done || exit 1

This will cause the code to exit if the for loop exits with a non-zero exit code.

As a point of trivia, the 1 in exit 1 is not needed. A plain exit command would exit with the exit status of the last executed command which would be false (code=1) if the download fails. If the download succeeds, the exit code of the loop is the exit code of the echo command. echo normally exits with code=0, signally success. In that case, the || does not trigger and the exit command is not executed.

Lastly, note that set -o errexit can be full of surprises. For a discussion of its pros and cons, see Greg's FAQ #105.

Documentation

From man bash:

for (( expr1 ; expr2 ; expr3 )) ; do list ; done
First, the arithmetic expression expr1 is evaluated according the rules described below under ARITHMETIC EVALUATION. The arithmetic expression expr2 is then evaluated repeatedly until it evaluates to zero. Each time expr2 evaluates to a non-zero value, list is executed and the arithmetic expression expr3 is evaluated. If any expression is omitted, it behaves as if it evaluates to 1. The return value is the exit status of the last command in list that is executed, or false if any of the expressions is invalid. [Emphasis added]

Related Question