Я изучаю содержимое этого файла 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 также удаляет два каталога.
Является ли это правильным или я что-то не хватает?
Из 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
функции при возникновении ошибок.
trap 'exit' ERR
set -e
останавливает выполнение script, если в команде или конвейере есть ошибка - что является противоположностью поведения оболочки по умолчанию, которое должно игнорировать ошибки в скриптах. Введите help set
в терминал, чтобы увидеть документацию для этой встроенной команды.
set -o pipefail
которую можно использовать для распространения ошибок, чтобы возвращаемое значение команды конвейера отличалось от нуля, если одна из предыдущих команд вышла с ненулевым статусом.
В соответствии с 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
Я нашел этот вопрос во время 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
Это старый вопрос, но ни один из ответов здесь не обсуждает использование 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
для этих скриптов, хотя это не всегда так.)
Во многих ситуациях, это то, что нужно отдельно следить за кодированием в обороне. Иногда вам нужно, например, пройти через временный файл, чтобы вы могли увидеть, успешно ли завершилась команда, которая произвела этот вывод, даже если идиома и удобство в противном случае наводят вас на использование конвейера.
Я полагаю, что цель сценария - быстро провалиться.
Чтобы проверить это самостоятельно, просто введите set -e
в set -e
строке bash. Теперь попробуйте запустить ls
. Вы получите список каталогов. Теперь введите lsd
. Эта команда не распознается и выдаст код ошибки, поэтому ваша подсказка bash закроется (из-за set -e
).
Теперь, чтобы понять это в контексте "скрипта", используйте этот простой скрипт:
#!/bin/bash
# set -e
lsd
ls
Если вы запустите его как есть, вы получите список каталогов из ls
в последней строке. Если вы раскомментируете set -e
и запустите его снова, вы не увидите список каталогов, поскольку bash прекращает обработку, когда обнаруживает ошибку от lsd
.