Что означает set -e в скрипте bash?

484

Я изучаю содержимое этого файла preinst, который выполняется script, прежде чем этот пакет распакуется из файла архива Debian (.deb).

script имеет следующий код:

#!/bin/bash
set -e
# Automatically added by dh_installinit
if [ "$1" = install ]; then
   if [ -d /usr/share/MyApplicationName ]; then
     echo "MyApplicationName is just installed"
     return 1
   fi
   rm -Rf $HOME/.config/nautilus-actions/nautilus-actions.conf
   rm -Rf $HOME/.local/share/file-manager/actions/*
fi
# End automatically added section

Мой первый запрос касается строки:

set -e

Я думаю, что остальная часть script довольно проста: она проверяет, выполняет ли диспетчер пакетов Debian/Ubuntu операцию установки. Если да, то проверяет, было ли мое приложение только что установлено в системе. Если он есть, script печатает сообщение "MyApplicationName только что установлен" и заканчивается (return 1 означает, что он заканчивается "ошибкой", не так ли?).

Если пользователь запрашивает систему пакетов Debian/Ubuntu для установки моего пакета, script также удаляет два каталога.

Является ли это правильным или я что-то не хватает?

  • 28
    установить -е
  • 25
    причина, по которой вы не можете найти это в Google: -e в вашем запросе интерпретируется как отрицание. Попробуйте следующий запрос: bash set "-e"
Показать ещё 6 комментариев
Теги:
sh

6 ответов

497
Лучший ответ

Из help set:

  -e  Exit immediately if a command exits with a non-zero status.

Но некоторые считают, что это плохая практика (FAQ FAQ и так, например, люди, пишущие этот FAQ на irc freenode #bash), лучше использовать:

trap 'do_something' ERR

для запуска do_something функции при возникновении ошибок.

См. Http://mywiki.wooledge.org/BashFAQ/105

  • 11
    Что было бы с do_something, если бы я хотел использовать ту же семантику, что и «Немедленный выход, если команда завершается с ненулевым статусом»?
  • 60
    trap 'exit' ERR
Показать ещё 10 комментариев
63

set -e останавливает выполнение script, если в команде или конвейере есть ошибка - что является противоположностью поведения оболочки по умолчанию, которое должно игнорировать ошибки в скриптах. Введите help set в терминал, чтобы увидеть документацию для этой встроенной команды.

  • 31
    Он только останавливает выполнение, если последняя команда в конвейере имеет ошибку. Существует специфическая для Bash опция: set -o pipefail которую можно использовать для распространения ошибок, чтобы возвращаемое значение команды конвейера отличалось от нуля, если одна из предыдущих команд вышла с ненулевым статусом.
39

В соответствии с bash - Руководство по настройке Set, если -e/errexit установлено, оболочка немедленно выйдет, если a pipeline состоит из простой простой команды, список или составная команда возвращает ненулевой статус.

По умолчанию статус выхода конвейера является статусом выхода последней команды в конвейере, если только параметр pipefail не включен (по умолчанию он отключен).

Если это так, конвейеры возвращают статус последней (самой правой) команды для выхода с ненулевым статусом или ноль, если все команды успешно завершены.

Если вы хотите что-то выполнить при выходе, попробуйте определить trap, например:

trap onexit EXIT

где onexit - ваша функция, чтобы что-то сделать при выходе, например ниже, которая печатает простую трассировку стека :

onexit(){ while caller $((n++)); do :; done; }

Существует аналогичная опция -e/errtrace, которая вместо этого будет ловушка ERR, например:

trap onerr ERR

Примеры

Пример нулевого статуса:

$ true; echo $?
0

Пример нулевого статуса:

$ false; echo $?
1

Отрицательные статусные примеры:

$ ! false; echo $?
0
$ false || true; echo $?
0

Тест с pipefail отключен:

$ bash -c 'set +o pipefail -e; true | true | true; echo success'; echo $?
success
0
$ bash -c 'set +o pipefail -e; false | false | true; echo success'; echo $?
success
0
$ bash -c 'set +o pipefail -e; true | true | false; echo success'; echo $?
1

Тестирование с pipefail включено:

$ bash -c 'set -o pipefail -e; true | false | true; echo success'; echo $?
1
34

Я нашел этот вопрос во время Googling, пытаясь выяснить, что такое статус выхода для сценария, который был прерван из-за set -e. Ответ мне не казался очевидным; следовательно, этот ответ. В принципе, set -e прерывает выполнение команды (например, скрипт оболочки) и возвращает код состояния выхода из команды, которая не удалась (т.е. внутренний скрипт, а не внешний скрипт).

Например, предположим, что у меня есть сценарий оболочки outer-test.sh:

#!/bin/sh
set -e
./inner-test.sh
exit 62;

Код для inner-test.sh:

#!/bin/sh
exit 26;

Когда я запускаю outer-script.sh из командной строки, мой внешний скрипт завершается кодом выхода внутреннего скрипта:

$ ./outer-test.sh
$ echo $?
26
1

Это старый вопрос, но ни один из ответов здесь не обсуждает использование set -e aka set -o errexit в сценариях обработки пакетов Debian. Использование этой опции является обязательным в этих сценариях, в соответствии с политикой Debian; очевидно, цель состоит в том, чтобы избежать возможности необработанного условия ошибки.

На практике это означает, что вы должны понимать, при каких условиях выполняемые вами команды могут возвращать ошибку и явно обрабатывать каждую из этих ошибок.

Обычными getchas являются, например, diff (возвращает ошибку, когда есть разница) и grep (возвращает ошибку, когда нет совпадения). Вы можете избежать ошибок с явной обработкой:

diff this that ||
  echo "$0: there was a difference" >&2
grep cat food ||
  echo "$0: no cat in the food" >&2

(Обратите внимание также, как мы позаботимся о включении текущего имени сценария в сообщение и записи диагностических сообщений в стандартную ошибку вместо стандартного вывода.)

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

diff this that || true
grep cat food || :

(Использование оболочки : команда -o p немного неясна, но довольно часто видна.)

Чтобы повторить,

something || other

является сокращением для

if something; then
    : nothing
else
    other
fi

т.е. мы явно говорим, что other должно выполняться тогда и только тогда, когда something терпит неудачу. Операторы longhand if (и других команд управления потоком оболочки, такие как while, until), также являются допустимым способом обработки ошибки (действительно, если это не так, сценарии оболочки с set -e никогда не могут содержать инструкции управления потоком!)

А также, чтобы быть явным, в отсутствие такого обработчика set -e приведет к тому, что весь скрипт немедленно завершится с ошибкой, если diff обнаружит разницу, или если grep не нашел совпадения.

С другой стороны, некоторые команды не выдают статус выхода с ошибкой, когда вы захотите. Обычно проблемные команды find (статус выхода не отражает, были ли на самом деле нашли файлы) и sed (статус выхода не покажет, получил ли сценарий любой вход или на самом деле успешно выполняются любые команды). Простой охранник в некоторых сценариях - это передать команду, которая кричит, если нет выхода:

find things | grep .
sed -e 's/o/me/' stuff | grep ^

Следует отметить, что статус выхода конвейера - это статус выхода последней команды в этом конвейере. Таким образом, приведенные выше команды фактически полностью маскируют статус find и sed, и только говорят вам, наконец, удалось ли grep.

(Bash, конечно же, set -o pipefail, но скрипты пакета Debian не могут использовать функции Bash. Политика решительно диктует использование POSIX sh для этих скриптов, хотя это не всегда так.)

Во многих ситуациях, это то, что нужно отдельно следить за кодированием в обороне. Иногда вам нужно, например, пройти через временный файл, чтобы вы могли увидеть, успешно ли завершилась команда, которая произвела этот вывод, даже если идиома и удобство в противном случае наводят вас на использование конвейера.

0

Я полагаю, что цель сценария - быстро провалиться.

Чтобы проверить это самостоятельно, просто введите set -e в set -e строке bash. Теперь попробуйте запустить ls. Вы получите список каталогов. Теперь введите lsd. Эта команда не распознается и выдаст код ошибки, поэтому ваша подсказка bash закроется (из-за set -e).

Теперь, чтобы понять это в контексте "скрипта", используйте этот простой скрипт:

#!/bin/bash 
# set -e

lsd 

ls

Если вы запустите его как есть, вы получите список каталогов из ls в последней строке. Если вы раскомментируете set -e и запустите его снова, вы не увидите список каталогов, поскольку bash прекращает обработку, когда обнаруживает ошибку от lsd.

  • 0
    Добавляет ли этот ответ какую-либо информацию или информацию, которая еще не была предоставлена другим по этому вопросу?
  • 0
    Я думаю, что он предлагает четкое, краткое объяснение функциональности, которого нет в других ответах. Ничего дополнительного, просто более сфокусированного, чем другие ответы.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню