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


437

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

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

например

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

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

8
В дополнение к set -e, также делать set -u(или set -eu). -uположил конец идиотскому, скрывающему ошибки поведению, в котором вы можете получить доступ к любой несуществующей переменной и получить пустое значение без диагностики.
— Каз

Ответы:


742

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

set -e

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

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

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


36
Это бы сработало, но мне нравится использовать "#! / Usr / bin / env bash", потому что я часто запускаю bash не из / bin. И "#! / Usr / bin / env bash -e" не работает. Кроме того, приятно иметь место для изменения, чтобы читать «set -xe», когда я хочу включить трассировку для отладки.
— Ville Laurikari

48
Кроме того, флаги в строке shebang игнорируются, если скрипт запускается как bash script.sh.
— Том Андерсон

27
Просто примечание: если вы объявляете функции внутри bash-скрипта, функция должна быть повторно объявлена ​​как set -e в теле функции, если вы хотите расширить эту функциональность.
— Джин Ким

8
Кроме того, если вы создадите свой сценарий, строка Шебанга будет бесполезной.

4
@JinKim Похоже, что это не так в Bash 3.2.48. Попробуйте следующее внутри скрипта: set -e; tf() { false; }; tf; echo 'still here'. Даже без set -eвнутренней части tf(), выполнение отменяется. Возможно, вы хотели сказать, что set -eне наследуется подоболочками , что является правдой.
— mklement0

202

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

Имейте в виду, что 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 | Тройник configure.log
делать

1
Спасибо за объяснение важности того, pipefailчтобы идти вместе с set -o!
— Малкольм

83

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

dosomething1 || exit 1

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

dosomething || true

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


1
Мне это нравится. Тем более, что главный ответ - bash-centric (мне совершенно не ясно, относится ли / в какой степени к сценариям zsh). И я мог бы это посмотреть, но ваша просто понятнее, потому что логика.
— g33kz0r

set -eне является bash-centric - он поддерживается даже на оригинальной Bourne Shell.
— Маркос Вивес дель Соль

27

Если у вас есть очистка, которую вы должны сделать при выходе, вы также можете использовать '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, и т. д.


12

$?Переменная редко. Псевдо-идиома 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

3
Почему «всегда должно быть написано как»? Я имею в виду, почему «так» должно быть? Когда команда длинная (например, вызывая GCC с дюжиной опций), тогда гораздо удобнее выполнить команду перед проверкой состояния возврата.
— 2012 г.

Если команда слишком длинная, вы можете разбить ее, назвав ее (определите функцию оболочки).
— Марк Эдгар

12

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

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


34
Чтобы потенциально спасти других, нужно прочитать help set: -uобрабатывает ссылки на неустановленные переменные как ошибки.
— mklement0

1
так или нет, set -uили set -eне оба? @lumpynose
— ericn

1
@eric Я уволился несколько лет назад. Хотя я любил свою работу, мой старый мозг все забыл. Я бы догадался, что вы могли бы использовать оба вместе; плохая формулировка с моей стороны; Я должен был сказать «и / или».
— lumpynose

3

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

dosomething1 && dosomething2 && dosomething3

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

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


-2

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

[[ `cmd` ]] && echo success_else_silence

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

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

[[ `mount | grep /dev/sda1` ]] && umount /dev/sda1 

5
Нет, [[ cmd`]] `это не одно и то же. Значение false, если вывод команды пустой, и true в противном случае, независимо от состояния выхода команды.
— Жиль "ТАК - перестань быть злым"
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.