Как заставить этот скрипт завершаться ошибкой в ​​зависимости от результата цикла for?

Как заставить этот скрипт завершаться ошибкой в ​​зависимости от результата цикла for?

У меня есть скрипт bash, который использует set -o errexitтак, что при ошибке весь скрипт завершается в точке сбоя.
Скрипт запускает curlкоманду, которая иногда не может получить нужный файл - однако, когда это происходит, скрипт не завершается с ошибкой.

Я добавил forпетлю к

  1. подождите несколько секунд, затем повторите curlкоманду
  2. используйте falseв конце цикла for для определения ненулевого статуса выхода по умолчанию — если команда curl выполнена успешно, цикл прерывается, а статус выхода последней команды должен быть равен нулю.
#! /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 .....

Проблема в том, что когда curlкоманда не выполняется, цикл повторяет команду пять раз — если все попытки безуспешны, цикл for завершается, и основной скрипт возобновляет работу — вместо того, чтобы запустить errexit.
Как мне заставить весь скрипт завершиться, если этот curlоператор не выполняется?

решение1

Заменять:

done

с:

done || exit 1

Это приведет к выходу кода, если forцикл завершится с ненулевым кодом выхода.

В качестве мелочи, 1in exit 1не нужен. Обычная exitкоманда завершит работу со статусом выхода последней выполненной команды, который будет false(code=1), если загрузка не удалась. Если загрузка прошла успешно, код выхода цикла будет кодом выхода команды echo. echoобычно завершается с кодом=0, что является сигналом успеха. В этом случае ||не срабатывает и exitкоманда не выполняется.

Наконец, обратите внимание, что set -o errexitможет быть полно сюрпризов. Для обсуждения его плюсов и минусов см.Часто задаваемые вопросы Грега #105.

Документация

От man bash:

для(( выражение1 ; выражение2 ; выражение3 )) ;делатьсписок ; сделанный
Сначала арифметическое выражение expr1 вычисляется в соответствии с правилами, описанными ниже в разделе АРИФМЕТИЧЕСКАЯ ОЦЕНКА. Затем арифметическое выражение expr2 вычисляется повторно, пока не станет равным нулю. Каждый раз, когда expr2 вычисляется как ненулевое значение, выполняется list и вычисляется арифметическое выражение expr3. Если какое-либо выражение опущено, оно ведет себя так, как будто оно равно 1. Возвращаемое значение — это статус завершения последней выполненной команды в списке или false, если какое-либо из выражений недопустимо.[Выделено мной]

решение2

Если вы предпочитаете остановить цикл при первой ошибке, а также избежать set -e, вы можете сделать что-то вроде этого:

for i in `seq 1 10`; do
  run_command || exit 1;
done

решение3

Если вы установили errexit, то falseоператор должен вызвать немедленный выход из скрипта. То же самое, если curlкоманда не удалась.

Ваш пример скрипта, как он написан, должен завершить работу после первой curlошибки команды при первом вызове, falseесли установлен errexit.

Чтобы увидеть, как это работает (я использую сокращение -eдля установки errexit):

$ ( set -e;  false; echo still here )
$

$ ( set +e;  false; echo still here )
still here
$

Таким образом, если curlкоманда выполняется более одного раза, этот скрипт не установлен errexit.

решение4

set -o errexitможет быть сложным в циклах и подоболочках, поскольку вам придется пройти путь обратно из процесса.

Разрыв цикла (даже при нормальной работе) считается плохой практикой. Можете называть меня старомодным, если я предпочитаю цикл while циклу for для двух условий, но я считаю, что лучше читать:

i=1
RET=-1
while [ $i -le 5 ] && [ $RET -ne 0 ]; do
    [ $i -eq 1 ] || sleep 5
    echo "attempt number: "$i
    curl -LSso ~/.vim/autoload/pathogen.vim https://tpo.pe/pathogen.vim
    RET=$?
    i=$((i+1))
done
exit $RET

Связанный контент