Как проверить состояние выхода с помощью оператора if

256

Мне было интересно, что будет лучшим способом проверить состояние выхода в операторе if, чтобы повторить конкретный вывод.

Я думаю об этом

if [ $? -eq 1 ]
then
   echo "blah blah blah"
fi

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

deadcell4
источник
3
Пожалуйста, оставьте свой полный сценарий (или, по крайней мере, более широкую область). В остальном это выглядит нормально.
RedX
5
Если вам нужно использовать код выхода из определенного вызова программы в двух разных местах, то вам нужно сохранить его - что-то вродеsome_program; rc=$?; if [ ${rc} -eq 1 ] .... fi ; exit ${rc}
twalberg

Ответы:

262

Каждая команда, которая выполняется, имеет статус выхода.

Эта проверка проверяет состояние завершения команды, завершившейся совсем недавно до запуска этой строки.

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

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

if some_command; then
    echo command returned true
else
    echo command returned some error
fi

Или перевернуть это использовать !для отрицания

if ! some_command; then
    echo command returned some error
else
    echo command returned true
fi

Обратите внимание , однако , что ни один из этих забот , что код ошибки. Если вы знаете, что заботитесь только о конкретном коде ошибки, вам нужно проверить это $?вручную.

Этан Рейснер
источник
11
@ deadcell4 Когда нужно завершить сценарий оболочки при сбое программы, полезна следующая идиомаa_command || return 1
gboffi
10
@gboffi returnработает только в функции и скрипте с источником. Вы нуждаетесь exitдля другого случая (который делает слишком много в функции и исходном скрипте). Но да, это, безусловно, разумная модель, если вам не нужна какая-то особая очистка или дополнительный вывод.
Этан Рейснер
1
Я должен сказать, что dashнеинтерактивная оболочка по умолчанию во многих современных дистрибутивах Linux не заботится о различии между returnи exitвнутри исполняемых скриптов оболочки. dashвыходит из сценария, даже если я использую returnв нем.
Гбоффи
Какова логика последних двух проверок? Кажется нелогичным, что условие выполняется, if <command>если код выхода равен 0. На любом другом языке было бы наоборот
sjw
3
ВАЖНОЕ ПРИМЕЧАНИЕ: это не будет работать для труб. if ! some_command | some_other_commandбудет игнорировать статус some_command. Два наиболее обходных обхода команды - set -o pipefail(могут изменить функциональность в других частях вашей программы) или переместить ifоператор if [[ ${PIPESTATUS[0]} -ne 0 ]]в качестве отдельной последующей команды (некрасиво, но функционально). Если вы используете set -eто, вы также захотите добавить || trueв конец канала при использовании второго решения, поскольку удаление канала из предлагаемого потока управления ifв противном случае приведет к его немедленному выходу.
Алекс Янсен
186

Обратите внимание, что коды выхода! = 0 используются для сообщения об ошибке. Итак, лучше сделать:

retVal=$?
if [ $retVal -ne 0 ]; then
    echo "Error"
fi
exit $retVal

вместо того

# will fail for error codes > 1
retVal=$?
if [ $retVal -eq 1 ]; then
    echo "Error"
fi
exit $retVal
Oo.oO
источник
Вы должны проверить на retVal, потому что $? после присвоения retVal не возвращается значение вашей команды.
anr78
Не совсем: mywiki.wooledge.org/BashFAQ/002 - однако я согласен, что редактирование улучшает читабельность.
Oo.oO
1
Только что нашел этот пост, который объясняет это stackoverflow.com/questions/20157938/…
anr78
dnf check-updateвозвращает 0 (без обновлений), 100 (доступные обновления) или 1 (ошибка).
jww
1
@jww - ну, это не очень хорошая идея - идти против соглашения ( gnu.org/software/bash/manual/html_node/Exit-Status.html ). Но, ну, ничто не мешает этому. Если dnfразработчики выбрали этот путь, это их выбор. Но, тем не менее, их выбор не делает нарушение спецификации :)
Oo.oO
44

$?это параметр, как и любой другой. Вы можете сохранить его значение, чтобы использовать его перед вызовом exit.

exit_status=$?
if [ $exit_status -eq 1 ]; then
    echo "blah blah blah"
fi
exit $exit_status
chepner
источник
29

Альтернатива явному ifутверждению

Минимально:

test $? -eq 0 || echo "something bad happened"

Complete:

EXITCODE=$?
test $EXITCODE -eq 0 && echo "something good happened" || echo "something bad happened"; 
exit $EXITCODE
Catskul
источник
13

Просто чтобы добавить к полезному и подробному ответу :

Если вам нужно явно проверить код выхода, лучше использовать арифметический оператор (( ... )), вот так:

run_some_command
(($? != 0)) && { printf '%s\n' "Command exited with non-zero"; exit 1; }

Или используйте caseутверждение:

run_some_command; ec=$?  # grab the exit code into a variable so that it can
                         # be reused later, without the fear of being overwritten
case $ec in
    0) ;;
    1) printf '%s\n' "Command exited with non-zero"; exit 1;;
    *) do_something_else;;
esac

Связанный ответ об обработке ошибок в Bash:

codeforester
источник