9

Как в Bash-скрипте выйти из всего скрипта при выполнении определенного условия?

14

Я пишу скрипт на Bash для тестирования кода. Однако кажется бессмысленным запускать тесты, если компиляция кода завершается неудачей. В этом случае я просто хочу прекратить выполнение тестов.

Есть ли способ сделать это без оборачивания всего скрипта в цикл и использования операторов break? Что-то наподобие goto?

5 ответ(ов)

8

Для управления ошибками в Bash-скриптах можно использовать команду set -e. Эта команда позволяет завершать выполнение скрипта сразу после первой команды, которая вернула ненулевой код возврата (ошибку).

Пример использования set -e:

#!/bin/bash

set -e

/bin/command-that-fails
/bin/command-that-fails2

В этом примере, если первая команда command-that-fails завершится с ошибкой, скрипт сразу прекратит выполнение, и вторая команда command-that-fails2 не будет выполняться.

Если бы вы проверяли статус возврата каждой команды, ваш скрипт выглядел бы следующим образом:

#!/bin/bash

# Предполагаю, что вы используете make

cd /project-dir
make
if [[ $? -ne 0 ]] ; then
    exit 1
fi

cd /project-dir2
make
if [[ $? -ne 0 ]] ; then
    exit 1
fi

С использованием set -e ваш скрипт можно записать более лаконично:

#!/bin/bash

set -e

cd /project-dir
make

cd /project-dir2
make

Таким образом, если какая-либо команда завершится неудачно, весь скрипт также завершится с ошибкой, и вы сможете проверить статус завершения, используя переменную $?. Это особенно удобно, если ваш скрипт длинный или если вы выполняете много команд, так как не нужно добавлять проверки статуса выполнения для каждой строки.

0

Если вы вызовете скрипт с помощью source, вы можете использовать return <x>, где <x> будет статусом завершения скрипта (используйте ненулевое значение для ошибки или false). Однако, если вы вызываете исполняемый скрипт (т.е. непосредственно по его имени), оператор return вызовет ошибку с сообщением "return: can only `return' from a function or sourced script".

Если вместо этого использовать exit <x>, когда скрипт вызывается с помощью source, это приведет к выходу из оболочки, которая запустила скрипт, а исполняемый скрипт просто завершится, как и ожидалось.

Чтобы обрабатывать оба случая в одном скрипте, вы можете использовать

return <x> 2> /dev/null || exit <x>

Это будет работать для любого из методов вызова. Предполагается, что вы будете использовать эту конструкцию на верхнем уровне скрипта. Я бы не рекомендовал напрямую выходить из скрипта изнутри функции.

Примечание: <x> должен быть просто числом.

0

В вашем вопросе вы описываете обычный подход обработки ошибок в скриптах с помощью функции run(), который действительно может быть полезен. Основная идея заключается в том, что вам нужно избежать скрытого завершения скрипта при ошибках, что может произойти, если использовать set -e.

Ваш код выглядит довольно правильно и эффективно решает поставленную задачу. Функция run() принимает строку с командой, выполняет её и проверяет код возврата. В случае неудачи, она сообщает об этом и завершает выполнение скрипта, а в случае успеха — выводит результат выполнения команды.

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

function run() {
  cmd_output=$(eval "$1")
  return_value=$?
  if [ $return_value -ne 0 ]; then
    echo "Команда '$1' завершилась с ошибкой"
    exit 1
  else
    echo "Вывод: $cmd_output"
    echo "Команда выполнена успешно."
  fi
  return $return_value
}

run "date"
run "false"
run "date"

Обратите внимание на изменения:

  1. Использование "$1" при передаче переменной в eval — это помогает избежать проблем с пробелами и специальные символами в командной строке.
  2. Я изменил оператор сравнения на -ne, который более привычен при проверке целых чисел.
  3. Сообщение об ошибке теперь также содержит само название команды, что может быть полезно для отладки.

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

0

Я тоже задавался этим вопросом, но не могу его задать, так как это будет дубликат.

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

Вот небольшой пример, как это сделать:

#!/bin/bash

boom() {
    while true; do sleep 1.2; echo boom; done
}

f() {
    echo Hello
    N=0
    while
        ((N++ <10))
    do
        sleep 1
        echo $N
        #        ((N > 5)) && exit 4 # не работает
        ((N > 5)) && { kill -9 $$; exit 5; } # работает 
    done
}

boom &
f &

while true; do sleep 0.5; echo beep; done

Это более полноценный ответ, но все же неполный, так как я действительно не знаю, как избавиться от части с boom.

0

Вы можете закрыть вашу программу по ее имени следующим образом:

Для мягкого выхода используйте

pkill -9 -x programname # Замените "programname" на имя вашей программы

Для жесткого выхода используйте

pkill -15 -x programname # Замените "programname" на имя вашей программы

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

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь