Как выйти, если команда не удалась? - PullRequest
177 голосов
/ 29 сентября 2010

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

my_command && (echo 'my_command failed; exit)

но это не работает.Он продолжает выполнять инструкции, следующие за этой строкой в ​​скрипте.Я использую Ubuntu и Bash.

Ответы [ 9 ]

335 голосов
/ 29 сентября 2010

Попробуйте:

my_command || { echo 'my_command failed' ; exit 1; }

Четыре изменения:

  • Изменить && на ||
  • Используйте { } вместо ( )
  • Введите ; после exit и
  • пробелы после { и до }

Поскольку вы хотите распечатать сообщение и выйти только в случае сбоя команды (выход с ненулевым значением), вам нужно ||, а не &&.

cmd1 && cmd2

будет работать cmd2, когда cmd1 завершится успешно (выходное значение 0). Где, как

cmd1 || cmd2

будет работать cmd2 при сбое cmd1 (значение выхода не равно нулю).

Использование ( ) заставляет команду внутри них запускаться в субоболочке , а вызов exit оттуда заставляет вас выйти из субоболочки, а не из оригинала shell, следовательно, выполнение продолжается в исходной оболочке.

Чтобы преодолеть это использование { }

Последние два изменения требуются bash.

117 голосов
/ 29 сентября 2010

Другие ответы хорошо охватили прямой вопрос, но вас также может заинтересовать использование set -e.При этом любая команда, которая не выполняется (вне определенного контекста, например if tests), приведет к прерыванию работы сценария.Для некоторых скриптов это очень полезно.

56 голосов
/ 19 октября 2013

Если вы хотите такое поведение для всех команд в вашем скрипте, просто добавьте

  set -e 
  set -o pipefail

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

Однако это не позволяет печатать сообщение о выходе.

55 голосов
/ 29 сентября 2010

Обратите внимание, что состояние выхода каждой команды хранится в переменной оболочки $?, Которую вы можете проверить сразу после запуска команды. Ненулевое состояние означает сбой:

my_command
if [ $? -eq 0 ]
then
    echo "it worked"
else
    echo "it failed"
fi
12 голосов
/ 18 декабря 2012

Я взломал следующую идиому:

echo "Generating from IDL..."
idlj -fclient -td java/src echo.idl
if [ $? -ne 0 ]; then { echo "Failed, aborting." ; exit 1; } fi

echo "Compiling classes..."
javac *java
if [ $? -ne 0 ]; then { echo "Failed, aborting." ; exit 1; } fi

echo "Done."

Перед каждой командой следует информационное эхо, и следуйте каждой команде в той же строке
if [ $? -ne 0 ];....(Конечно, вы можете отредактировать это сообщение об ошибке, если хотите.)

10 голосов
/ 29 сентября 2010

Если my_command канонически спроектировано, , т. Е. возвращает 0 в случае успеха, тогда && полностью противоположно тому, что вы хотите. Вы хотите ||.

Также обратите внимание, что ( мне не кажется правильным в bash, но я не могу попробовать оттуда, где я нахожусь. Скажи мне.

my_command || {
    echo 'my_command failed' ;
    exit 1; 
}
3 голосов
/ 09 марта 2018

Встроенная оболочка trap позволяет перехватывать сигналы и другие полезные условия, включая неудачное выполнение команды (т. Е. Ненулевой статус возврата). Поэтому, если вы не хотите явно проверять состояние возврата каждой отдельной команды, вы можете сказать trap "your shell code" ERR, и код оболочки будет выполняться каждый раз, когда команда возвращает ненулевой статус. Например:

trap "echo script failed; exit 1" ERR

Обратите внимание, что, как и в других случаях перехвата неудачных команд, конвейеры требуют специальной обработки; выше не поймают false | true.

3 голосов
/ 08 марта 2018

Вы также можете использовать, если хотите сохранить статус ошибки выхода и иметь читаемый файл с одной командой на строку:

my_command1 || exit $?
my_command2 || exit $?

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

0 голосов
/ 29 декабря 2017

Следующие функции отображают ошибки только в случае сбоя команды:

silently () {
    label="${1}"
    command="${2}"
    error=$(eval "${command}" 2>&1 >"/dev/null")

    if [ ${?} -ne 0 ]; then
        echo "${label}: ${error}" >&2
        exit 1
    fi
}
...