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

404

Я пишу script в Bash для проверки кода. Однако, кажется, глупо запускать тесты, если компиляция кода не выполняется в первую очередь, и в этом случае я просто прекращу тесты.

Есть ли способ сделать это, не обертывая весь script внутри цикла while и используя разрывы? Что-то вроде dun dun dun goto?

Теги:
scripting
exit-code
exit

6 ответов

378
Лучший ответ

Попробуйте следующее утверждение:

exit 1

Замените 1 соответствующими кодами ошибок. См. Также Коды выхода со специальными значениями.

  • 4
    @ MichałGórny, что было бы хорошим кодом статуса?
  • 4
    @CMCDragonkai, обычно работает любой ненулевой код. Если вам не нужно ничего особенного, вы можете просто использовать 1 последовательно. Если сценарий предназначен для запуска другим сценарием, вы можете определить свой собственный набор кодов состояния с определенным значением. Например, 1 == тесты не пройдены, 2 == ошибка компиляции. Если сценарий является частью чего-то другого, вам может потребоваться настроить коды в соответствии с используемыми там практиками. Например, когда часть набора тестов запускается automake, код 77 используется для обозначения пропущенного теста.
Показать ещё 3 комментария
470

Используйте set -e

#!/bin/bash

set -e

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

script завершится после первой строки, которая не сработает (возвращает ненулевой код выхода). В этом случае command-that-fail2 не будет работать.

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

#!/bin/bash

# I'm assuming you're using 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

Любая сбой команды приведет к сбою всего script и возвращает статус выхода, который вы можете проверить с помощью $?. Если ваш script очень длинный или вы создаете много вещей, он будет довольно уродливым, если вы все равно добавите проверки статуса возврата.

  • 8
    С set -e Вы все еще можете сделать некоторые команды выхода с ошибками без остановки сценария: command 2>&1 || echo $? ,
  • 1
    Я думаю, что это не будет ловить ошибки, если у вас есть такие команды, как (cd tmp && make)
Показать ещё 7 комментариев
118

Плохой парень SysOps однажды научил меня технике с тремя пальцами:

yell() { echo "$0: $*" >&2; }
die() { yell "$*"; exit 111; }
try() { "$@" || die "cannot $*"; }

Эти функции: * NIX OS и оболочка - надежные. Поместите их в начало вашего script (bash или иначе), try() вашего оператора и кода.

Объяснение

(на основе летящих овец).

  • yell: напечатайте имя script и все аргументы stderr:
    • $0 - это путь к script;
    • $* - все аргументы.
    • >&2 означает > перенаправление stdout to и pipe 2. pipe 1 будет stdout.
  • die делает то же самое, что и yell, но выходит с не-0 статусом выхода, что означает "сбой".
  • try использует || (boolean OR), который оценивает только правую сторону, если левая не сработала.
    • $@ - все аргументы снова, но разные.

Надеюсь, что все объясняет.

  • 3
    Я довольно новичок в сценариях Unix. Не могли бы вы объяснить, как выполняются вышеуказанные функции? Я вижу новый синтаксис, с которым я не знаком. Благодарю.
  • 11
    yell : $0 - это путь к сценарию. $* все аргументы. >&2 означает « > перенаправить стандартный вывод на & pipe 2 ». труба 1 была бы самой stdout. поэтому yell префиксирует все аргументы с именем скрипта и печатает в stderr. die делает то же самое, что и yell , но выходит со статусом выхода не равным 0, что означает «сбой». попробуйте использовать логическое или || , который оценивает только правую сторону, если левая не провалилась. $@ снова все аргументы, но разные . надеюсь, что это все объясняет
Показать ещё 4 комментария
7

Если вы вызовете script с помощью source, вы можете использовать return <x>, где <x> будет статусом выхода script (используйте ненулевое значение для ошибки или false). Это также будет работать, как ожидалось, когда вы source script. Если вы вызываете исполняемый файл script (т.е. Напрямую с его именем файла), оператор return приведет к жалобе (сообщение об ошибке "return: может только" возвращать "из функции или источника script" ).

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

Чтобы обрабатывать любой случай в том же script, вы можете использовать

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

Это будет обрабатывать любой вызов, который может быть подходящим.

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

  • 0
    У меня не работает в скрипте с возвратом / выходом внутри функции, т. Е. Возможно ли выйти внутри функции без существующей оболочки, но без того, чтобы вызывающая функция заботилась о правильной проверке кода возврата функции ?
  • 0
    @jan То, что вызывающая сторона делает (или не делает) с возвращаемыми значениями, полностью ортогонально (т. е. не зависит от) того, как вы возвращаетесь из функции (... без выхода из оболочки, независимо от того, какой вызов). Это в основном зависит от кода вызывающего абонента, который не является частью этого Q & A. Вы даже можете адаптировать возвращаемое значение функции к потребностям вызывающего, но этот ответ не ограничивает, каким может быть это возвращаемое значение ...
6

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

function run() {
  cmd_output=$(eval $1)
  return_value=$?
  if [ $return_value != 0 ]; then
    echo "Command $1 failed"
    exit -1
  else
    echo "output: $cmd_output"
    echo "Command succeeded."
  fi
  return $return_value
}
run "date"
run "false"
run "date"
  • 1
    Чувак, мне почему-то очень нравится этот ответ. Я понимаю, что это немного сложнее, но это кажется таким полезным. И, учитывая, что я не эксперт по Bash, это заставляет меня поверить, что моя логика неверна, и что-то не так с этой методологией, в противном случае, я чувствую, что другие бы дали ей больше похвалы. Итак, в чем проблема с этой функцией? Есть ли что-то, что я должен искать здесь?
  • 0
    Я не помню причину использования eval, функция отлично работает с cmd_output = $ ($ 1)
Показать ещё 3 комментария
4

Вместо конструкции if вы можете использовать оценку короткого замыкания:

#!/usr/bin/env bash

echo $[1+1]
echo $[2/0]              # division by 0 but execution of script proceeds
echo $[3+1]
(echo $[4/0]) || exit $? # script halted with code 1 returned from `echo`
echo $[5+1]

Обратите внимание на пару круглых скобок, которая необходима из-за приоритета оператора чередования. $? - это специальный набор переменных для выхода из кода последней вызванной команды.

  • 1
    если я сделаю command -that --fails || exit $? это работает без скобок, что насчет echo $[4/0] что заставляет нас нуждаться в них?
  • 1
    @Anentropic @skalee Скобки не имеют ничего общего с приоритетом, а с обработкой исключений. Деление на ноль приведет к немедленному выходу из оболочки с кодом 1. Без скобок (т. echo $[4/0] || exit $? Простого echo $[4/0] || exit $? ) Bash никогда не выполнит echo , не говоря уже о том, чтобы подчиняться || ,

Ещё вопросы

Сообщество Overcoder
Наверх
Меню