Правильный способ обнаружения кода выхода оболочки при установке опции errexit
Я предпочитаю писать сплошную оболочку, поэтому набор errexit и nounset всегда установлен.
Следующий код будет остановлен в строке bad_command
#!/bin/bash
set -o errexit ; set -o nounset
bad_command # stop here
good_command
Я хочу его захватить, вот мой метод
#!/bin/bash
set -o errexit ; set -o nounset
rc=1
bad_command && rc=0 # stop here
[ $rc -ne 0 ] && do_err_handle
good_command
Есть ли лучший или более чистый метод
Мой ответ:
#!/bin/bash
set -o errexit ; set -o nounset
if ! bad_command ; then
# error handle here
fi
good_command
Ответы
Ответ 1
Как насчет этого? Если вы хотите получить код выхода...
#!/bin/sh
set -e
cat /tmp/doesnotexist && rc=$? || rc=$?
echo exitcode: $rc
cat /dev/null && rc=$? || rc=$?
echo exitcode: $rc
Вывод:
cat: /tmp/doesnotexist: No such file or directory
exitcode: 1
exitcode: 0
Ответ 2
Хранить с errexit. Это может помочь найти ошибки, которые в противном случае могли бы иметь непредсказуемые (и трудно обнаруживаемые) результаты.
#!/bin/bash
set -o errexit ; set -o nounset
bad_command || do_err_handle
good_command
Вышеуказанное будет работать нормально. errexit
требуется только, чтобы линия проходила, как и в случае с bad_command && rc=0
. Поэтому выше с помощью "или" будет выполняться только do_err_handle, если bad_command
завершается сбой, и пока do_err_handle не "сбой", то script будет продолжен.
Ответ 3
set -o errexit
bad_command || {
resp_code=$?
echo Bad Thing $resp_code happened
}
Ответ 4
Согласитесь с комментариями, поэтому, если вы можете отказаться от errexit
, вы можете легко сократить код до
bad_command || do_err_handle
good_command
Надеюсь, это поможет.
Ответ 5
Продолжайте писать твердый код оболочки.
Вы делаете это правильно, если bad_command действительно является командой.
Но будьте осторожны с вызовами функций в if, while, ||, && или!, потому что errexit там не работает. Это может быть опасно.
Если ваша bad_command действительно bad_function, вы должны написать это:
set -eu
get_exit_code() {
set +e
( set -e;
"[email protected]"
)
exit_code=$?
set -e
}
...
get_exit_code bad_function
if [ "$exit_code" != 0 ]; then
do_err_handle
fi
Это хорошо работает в bash 4.0. В bash 4.2 вы получаете только коды выхода 0 или 1.
Ответ 6
Обычный способ избежать выхода из программы Bash, когда errexit
установлен, а команда, которая может выйти из строя, запускается, должна предшествовать команде с !
.
После выполнения команды $?
не содержит статуса выхода команды. (Он содержит 0, если команда не выполнена, и 1 в противном случае.) Однако массив PIPESTATUS содержит статус выхода команды. Безопасным способом захвата статуса выхода команды, которая может выйти из строя, независимо от того, установлен ли или нет errexit
, является:
! bad_command
rc=${PIPESTATUS[0]}
Вторая строка может быть упрощена до rc=$PIPESTATUS
, но Shellcheck будет жаловаться на нее.
Если (как это часто бывает), вам не нужно знать статус выхода команды, только если это удалось или не удалось, тогда решение @george хорошо, если обработчик ошибок является однострочным. Для многострочных обработчиков ошибок хороший вариант:
if ! bad_command ; then
# Handle errors here
fi
Обратите внимание, что (кроме очень необычных обстоятельств) было бы не корректно использовать `bad_command`
(как предложено в вопросе) вместо простого bad_command
. Вы можете использовать ${PIPESTATUS[0]}
, чтобы получить статус завершения команды, если это необходимо в коде обработки ошибок, поскольку $?
также не содержит его в этом случае.
Ответ 7
что, если вы хотите узнать статус выхода bad_command?
Я думаю, что самый простой способ - отключить errexit:
#!/bin/sh
set -o errexit
some_code_here
set +o errexit
bad_command
status=$?
set -o errexit
process $status
Ответ 8
В bash вы можете использовать встроенную ловушку, которая довольно приятна. Видеть
https://unix.stackexchange.com/questions/79648/how-to-trigger-error-using-trap-command
Не уверен, насколько он переносится с другими оболочками.. поэтому YMMV
Ответ 9
Я собрал вместе (надеюсь) учебник из всех ответов:
#!/usr/bin/env bash
# exit immediately on error
set -o errexit
file='dfkjlfdlkj'
# Turn off 'exit immediately on error' during the command substitution
blah=$(set +o errexit && ls $file) && rc=$? || rc=$?
echo $blah
# Do something special if $rc
(( $rc )) && echo failure && exit 1
echo success
Ответ 10
Небольшая вариация ответа от @rrauenza. Так как && rc=$?
Частично, их ответ всегда будет равен && rc=0
Можно также установить rc
в 0
перед запуском команды. На мой взгляд, результат оказывается более читабельным, потому что переменная определяется заранее в собственной строке кода и изменяется только в том случае, если команда завершается с ненулевым состоянием выхода. Если также указано имя nounset
, то теперь ясно, что rc
действительно никогда не был неопределенным. Это также позволяет избежать смешивания &&
и ||
в той же строке, что может сбивать с толку, потому что не всегда можно знать приоритет оператора наизусть.
#!/bin/sh
set -eu
rc=0
cat /tmp/doesnotexist || rc=$?
echo exitcode: $rc
rc=0
cat /dev/null || rc=$?
echo exitcode: $rc
Выход:
cat: /tmp/doesnotexist: No such file or directory
exitcode: 1
exitcode: 0
Ответ 11
Чистый надежный способ выхода из ошибки
command_that_error_exits || { echo "Line $LINENO: Failed with Error" 1>&2; exit 1;}
Ответ 12
Принятый ответ хорош, но я думаю, что он может быть реорганизован, чтобы быть еще лучше; более общий, проще рефакторинг и читать:
some_command_status=$(some_command && echo $? || echo $?)
против.
some_command && some_command_status=$? || some_command_status=$?