Как в Bash-скрипте выйти из всего скрипта при выполнении определенного условия?
Я пишу скрипт на Bash для тестирования кода. Однако кажется бессмысленным запускать тесты, если компиляция кода завершается неудачей. В этом случае я просто хочу прекратить выполнение тестов.
Есть ли способ сделать это без оборачивания всего скрипта в цикл и использования операторов break? Что-то наподобие goto?
5 ответ(ов)
Для управления ошибками в 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
Таким образом, если какая-либо команда завершится неудачно, весь скрипт также завершится с ошибкой, и вы сможете проверить статус завершения, используя переменную $?
. Это особенно удобно, если ваш скрипт длинный или если вы выполняете много команд, так как не нужно добавлять проверки статуса выполнения для каждой строки.
Если вы вызовете скрипт с помощью 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>
должен быть просто числом.
В вашем вопросе вы описываете обычный подход обработки ошибок в скриптах с помощью функции 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"
при передаче переменной вeval
— это помогает избежать проблем с пробелами и специальные символами в командной строке. - Я изменил оператор сравнения на
-ne
, который более привычен при проверке целых чисел. - Сообщение об ошибке теперь также содержит само название команды, что может быть полезно для отладки.
Этот подход в целом хорош для простых сценариев, однако в сложных скриптах вы можете рассмотреть более расширенные механизмы обработки ошибок, такие как исключения или использование библиотеки для более безопасного выполнения команд.
Я тоже задавался этим вопросом, но не могу его задать, так как это будет дубликат.
Принятый ответ, использующий 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.
Вы можете закрыть вашу программу по ее имени следующим образом:
Для мягкого выхода используйте
pkill -9 -x programname # Замените "programname" на имя вашей программы
Для жесткого выхода используйте
pkill -15 -x programname # Замените "programname" на имя вашей программы
Если вы хотите узнать, как оценить условия для закрытия программы, вам следует уточнить ваш вопрос.
Как узнать, существует ли файл в Bash?
Как разобрать аргументы командной строки в Bash?
Как запросить ввод Yes/No/Cancel в скрипте оболочки Linux?
Как объявить и использовать логические переменные в shell-скрипте?
Как работает "cat << EOF" в bash?