Як перевірити стан виходу за допомогою оператора if


255

Мені було цікаво, що було б найкращим способом перевірити стан виходу в операторі if, щоб відзвучити конкретний вихід.

Я думаю про це

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

Проблема, яка у мене також є, полягає в тому, що оператор вихід є перед оператором if просто тому, що він повинен мати цей вихідний код. Крім того, я знаю, що роблю щось не так, оскільки вихід очевидно вийшов з програми.


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.
gboffi

Яка логіка останніх двох перевірок? Протиінтуїтивним здається, що умова 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

Ви повинні протестувати на 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

29

Альтернатива явній if заяві

Мінімально:

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

Повна:

EXITCODE=$?
test $EXITCODE -eq 0 && echo "something good happened" || echo "something bad happened"; 
exit $EXITCODE

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:

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.