В следующем скрипте есть синтаксическая ошибка или какая-то другая ошибка:
#!/usr/bin/env bash
set -euo pipefail
if [ ! -f /custom.log]; then
echo "test"
fi
abcxyz
Скрипт завершается ошибкой, выводится:
./test.sh: line 4: [: missing `]'
./test.sh: line 7: abcxyz: command not found
Меня не волнует, как исправить этот скрипт, но как я могу предотвратить дальнейшее выполнение скрипта, если он столкнется с этой ошибкой? Я бы подумал, что set -e
это должно обеспечить такое поведение.
решение1
set -e
не срабатывает при неудачном выполнении команд, которые используются в качестве условий, например, в разделе условий конструкций if
/ while
/ until
или слева от ||
, &&
или в функциях, подоболочках, исходных файлах, eval
ed-коде, которые вызываются при этих условиях.
Если это так, то:
if [ ! -f /custom.log ]; then
/custom.log
Если бы это был обычный файл, скрипт бы вышел из работы , поскольку [
тогда он также вышел бы с ненулевым статусом выхода.
Встроенная [
команда оболочки bash
(и большинства других реализаций) завершается со 1
статусом, если проверяемое условие не выполнено, а также 2
если обнаружена синтаксическая ошибка (хотя это не все синтаксические ошибки, например, не в [ -v 'a[+]' ]
).POSIX требует, чтобы в случае ошибки статус выхода был больше 1..
Таким образом, вы можете выбрать выход из скрипта, если команда завершается с кодом больше 1, независимо от того, используется ли она в условии или нет, с помощью чего-то вроде:
shopt -s extdebug # make sure the DEBUG trap propagates to subshells
trap '(($?>1 && (ret=$?))) && exit "$ret"' DEBUG
[ -f / ] || echo / not a regular file # OK
[ -f /] || echo was a syntax error # causes an exit, not output
echo not reached
Обратите внимание, что вы не можете использовать ERR
ловушку для этого, поскольку она ERR
срабатывает только в тех же условиях, что и те, которые запускают выход set -e
.
Теперь остерегайтесь последствий. Например, это может вызвать:
if grep -qs pattern /file; then
echo pattern was found in /file
fi
для выхода, если /file
не существует или не может быть прочитан, поскольку grep
в этом случае возвращается статус 2, хотя с -s
, намерение явно состояло в том, чтобы игнорировать эти случаи.
Поэтому вам нужно быть осторожным, при каких условиях команды, которые вы используете в своих условиях, могут завершиться со статусом больше 1. Чтобы обойти это, вам понадобится что-то вроде:
if sh -c 'grep -sq pattern / file || exit 1'; then...
Вы могли бы ограничитьвыход при статусе выхода больше 1к команде [
or test
что-то вроде:
unset -v previous_BASH_COMMAND
trap '
case $previous_BASH_COMMAND in
("[ "* | "test "*) (($?>1 && (ret=$?))) && exit "$ret"
esac
previous_BASH_COMMAND=$BASH_COMMAND' DEBUG
Это имеет несколько ограничений.
echo x
([ -f/]; echo y)
Это приведет к выходу из подоболочки, но не из родительской, поскольку $previous_BASH_COMMAND
там не было установлено. И в:
[ -f / ] && echo a regular file
(grep -qs foo /file && echo foo in /file)
echo here
Оболочка будет закрыта при запуске echo here
, поскольку $?
будет 2 и $previous_BASH_COMMAND
будет [ -f / ]
.
В любом случае, такие вещи, как
[ -f /] | cat
export var="$([ -f /])"
не удалось обнаружить, поскольку статус выхода не распространяется на родительский процесс оболочки (за исключением опции pipefail
в первом случае).
Теперь я не уверен, что стоит добавлять такое (хрупкое) обнаружение во время выполнения, когда ошибку легко обнаружить во время разработки (когда вы пишете и тестируете свой скрипт).