Что означает значение -e в bash script?

Я изучаю содержимое этого файла 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 также удаляет два каталога.

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

Ответ 1

Из help set:

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

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

trap 'do_something' ERR

запускать функцию do_something при возникновении ошибок.

Смотрите http://mywiki.wooledge.org/BashFAQ/105

Ответ 2

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

Ответ 3

Согласно bash - Руководству по построению набора, если установлено -e/errexit, оболочка немедленно завершается, если конвейер состоит из одной простой команды, список или составная команда возвращает ненулевой статус.

По умолчанию состояние выхода конвейера является состоянием выхода последней команды в конвейере, если только не включена опция 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

Ответ 4

Я нашел этот вопрос во время 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

Ответ 5

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

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

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

#!/bin/bash 
# set -e

lsd 

ls

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

Ответ 6

Script 1: without setting -e
#!/bin/bash
decho "hi"
echo "hello"
This will throw error in decho and program continuous to next line

Script 2: With setting -e
#!/bin/bash
set -e
decho "hi" 
echo "hello"
# Up to decho "hi" shell will process and program exit, it will not proceed further

Ответ 7

Это старый вопрос, но ни один из ответов здесь не обсуждает использование 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 для этих скриптов, хотя это не всегда так.)

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