Прерывание сценария оболочки, если какая-либо команда возвращает ненулевое значение?

У меня есть сценарий оболочки Bash, который вызывает ряд команд. Я хотел бы, чтобы скрипт оболочки автоматически выходил с возвращаемым значением 1, если какая-либо из команд возвращает ненулевое значение.

Возможно ли это без явной проверки результата каждой команды?

например

dosomething1
if [[ $? -ne 0 ]]; then
    exit 1
fi

dosomething2
if [[ $? -ne 0 ]]; then
    exit 1
fi

10 ответов

Решение

Добавьте это в начало сценария:

set -e

Это приведет к немедленному завершению работы оболочки, если простая команда завершится с ненулевым значением выхода. Простая команда - это любая команда, не являющаяся частью теста if, while или before, или частью && или || список.

Смотрите страницу руководства bash(1) во внутренней команде set для более подробной информации.

Я лично запускаю почти все сценарии оболочки с помощью команды "set -e". Это действительно раздражает иметь сценарий упорно продолжать, когда что-то не в середине и разбивает предположения для остальной части скрипта.

Добавить к принятому ответу:

Имейте в виду, что set -e иногда не достаточно, особенно если у вас есть трубы.

Например, предположим, у вас есть этот скрипт

#!/bin/bash
set -e 
./configure  > configure.log
make

... который работает как ожидалось: ошибка в configure прерывает выполнение.

Завтра вы делаете, казалось бы, тривиальные изменения:

#!/bin/bash
set -e 
./configure  | tee configure.log
make

... и теперь это не работает. Это объясняется здесь, и предоставляется обходной путь (только Bash):

#! / Bin/ Баш
установить -е 
set -o pipefail./configure | tee configure.log
делать

Операторы if в вашем примере не нужны. Просто сделайте это так:

dosomething1 || exit 1

Если вы воспользуетесь советами Вилле Лаурикари и используйте set -e тогда для некоторых команд вам может понадобиться использовать это:

dosomething || true

|| true заставит командный конвейер иметь true возвращаемое значение, даже если команда терпит неудачу, поэтому -e опция не убьет скрипт.

Если у вас есть очистка, которую вы должны сделать при выходе, вы также можете использовать 'trap' с псевдосигналом ERR. Это работает так же, как захват INT или любого другого сигнала; bash выдает ERR, если какая-либо команда завершается с ненулевым значением:

# Create the trap with   
#    trap COMMAND SIGNAME [SIGNAME2 SIGNAME3...]
trap "rm -f /tmp/$MYTMPFILE; exit 1" ERR INT TERM
command1
command2
command3
# Partially turn off the trap.
trap - ERR
# Now a control-C will still cause cleanup, but
# a nonzero exit code won't:
ps aux | grep blahblahblah

Или, особенно если вы используете "set -e", вы можете перехватить EXIT; Ваша ловушка будет выполняться, когда скрипт завершит работу по любой причине, включая нормальное завершение, прерывания, завершение, вызванное параметром -e, и т. д.

$? переменная редко нужна. Псевдо-идиома command; if [ $? -eq 0 ]; then X; fi всегда должен быть написан как if command; then X; fi,

Случаи, когда $? Требуется, когда необходимо проверить несколько значений:

command
case $? in
  (0) X;;
  (1) Y;;
  (2) Z;;
esac

или когда $? необходимо повторно использовать или иным образом манипулировать:

if command; then
  echo "command successful" >&2
else
  ret=$?
  echo "command failed with exit code $ret" >&2
  exit $ret
fi

Запустить его с -e или же set -e на вершине.

Также посмотрите на set -u,

В случае ошибки приведенный ниже сценарий напечатает КРАСНОЕ сообщение об ошибке и завершит работу.
Поместите это в начало вашего сценария bash:

      # BASH error handling:
#   exit on command failure
set -e
#   keep track of the last executed command
trap 'LAST_COMMAND=$CURRENT_COMMAND; CURRENT_COMMAND=$BASH_COMMAND' DEBUG
#   on error: print the failed command
trap 'ERROR_CODE=$?; FAILED_COMMAND=$LAST_COMMAND; tput setaf 1; echo "ERROR: command \"$FAILED_COMMAND\" failed with exit code $ERROR_CODE"; put sgr0;' ERR INT TERM
#!/bin/bash -e

должно хватить.

Выражение как

dosomething1 && dosomething2 && dosomething3

остановит обработку, когда одна из команд вернется с ненулевым значением. Например, следующая команда никогда не напечатает "done":

cat nosuchfile && echo "done"
echo $?
1

Просто добавлю еще один для справки, поскольку у Марка Эдгарса был дополнительный вопрос, и вот еще один пример, который затрагивает общую тему:

[[ `cmd` ]] && echo success_else_silence

который так же, как cmd || exit errcode как кто-то показал.

например. Я хочу убедиться, что раздел отключен, если смонтирован:

[[ `mount | grep /dev/sda1` ]] && umount /dev/sda1 
Другие вопросы по тегам