Как в 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" на имя вашей программы
Если вы хотите узнать, как оценить условия для закрытия программы, вам следует уточнить ваш вопрос.
Как объявить и использовать логические переменные в shell-скрипте?
Как работает "cat << EOF" в bash?
Как задать текущую рабочую директорию как директорию скрипта в Bash?
Как проверить статус завершения с помощью оператора 'if'
Не удается установить переменные в bash-скрипте [дублирующий вопрос]