Как остановить скрипт bash при сбое условия?

11

Здесь показано использование ||и &&в одной строке для объединения выполнения команд: Как я могу проверить наличие ошибок apt-get в скрипте bash?

Я пытаюсь остановить выполнение скрипта, если определенное условие не выполняется,

например

false || echo "Obvious error because its false on left" && exit

Здесь он печатает Obvious error because its false on the leftи выходит из консоли, что я и хотел.

true || echo "This shouldn't print" && exit

Здесь нет эхо-печати, но exitкоманда также выполняется, т. Е. Консоль закрыта, не должна ли exitкоманда не выполняться, потому что правая команда эха не была выполнена? Или по умолчанию оператор считается ложным в левой части &&оператора?

Редактировать: я должен был упомянуть об этом раньше, моя цель состояла в том, чтобы повторить ошибку и выйти, если она не была ясна. Что касается моего конкретного случая обнаружения ошибок при группировании условий с использованием && и ||, то @ bodhi.zazen ответ решает проблему.

Ответ @takatakatek проясняет управление потоком, а ссылки на руководство bash превосходны

Ответ @muru имеет хорошее объяснение, почему бы не использовать set -e, если вы хотите, чтобы пользовательские сообщения об ошибках генерировались с альтернативами использования perl и trap, что, я думаю, является более надежным способом, и я вижу, что использую его начиная со своего второго скрипта bash!

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

Ответы:

10

Вы, вероятно, хотите (как указано Steeldriver)

true || { echo "This shouldn't print" && exit; }

В противном случае ваш скрипт читает по одному условию за раз

А или В, а затем выйти

Я думаю, что вы хотите а или (б и выход)?

пантера
источник
4
На самом деле вы должны использовать { ... ; }(как предложено @steeldriver), иначе exitединственный выход из подоболочки, и родительская оболочка продолжит выполнение скрипта.
Гордон Дэвиссон
14

Проблема в том, что || и && пропускает только один последующий раздел цепочки команд, когда условие не выполняется. Если вы напишите полную блочную структуру, это имеет смысл. То, что вы написали, становится так:

if ! true ; then
   echo "This shouldn't print"
else
   exit
fi

То, что вы хотите, это:

if ! true ; then
   echo "This shouldn't print"
   exit
fi

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

takatakatek
источник
2
Да, на мой взгляд, это гораздо больше, как следует писать условия в bash
derHugo
@takatakatek Это делает более понятным, почему это терпит неудачу в моем случае. Я согласен, что логика в этом ясна, но это не причина для группировки условий с использованием && и || чтобы не выражать их с помощью условных операторов?
Косоль
@kosol Да, && и || объединить две вещи: тестирование состояния выхода предыдущей команды и указание одной команды (или группы команд), которую нужно выполнить (для &&) или пропустить (для ||). Они могут быть полезны для очень простых случаев, но они не могут заменить полностью выраженные if ... then ... elif ... else ... fiблоки. Я подозреваю, что вы можете не знать, что в Bash нет истинных логических типов, встречающихся в более сложных языках. Если статус выхода команды равен 0 (ноль), Bash воспринимает это как true / success . Если состояние выхода не равно нулю, Bash воспринимает это как ложное / неудачное .
Такатакатек
7

Самый простой способ потерпеть неудачу при ошибке - использовать -eопцию bash ( set -eили /bin/bash -eв shebang). Однако это не облегчает отправку пользовательских сообщений об ошибках. Есть пара идиом, которые могут сделать это проще:

die à la Perl

В Perl есть очень удобная dieкоманда, которая печатает сообщение в stderr и вызывает исключение, которое обычно приводит к завершению скрипта. Вы можете подражать этому:

die () {
  ret=$?
  print "%s\n" "$@" >&2
  exit "$ret"
}

false || die "Obvious error because its false on left"
true || die "This shouldn't print"

trap ERR

trap <command>Команда может быть использована для выполнения команды при получении сигнала, или при выходе из сценария ( trap ... EXIT), или когда команда возвращает ошибку ( trap ... ERR). Так что-то вроде:

trap 'ret=$?; printf "%s\n" "$ERR_MSG" >&2; exit "$ret"' ERR

Потом:

ERR_MSG="Obvious error because its false here"
false
ERR_MSG="This shouldn't print"
true

В любом случае, я бы предложил использовать хотя бы exit 1, чтобы указать, что сценарий не выполнен . Обычный пользователь exitбудет использовать состояние выхода предыдущей команды, которое в этих случаях будет командами echoor или printf, которые обычно выполняются успешно. Было бы неплохо сохранить статус завершения неудачной команды, как я это сделал здесь.

Мур
источник
6

Вы можете попробовать немного поэкспериментировать с запуском скрипта

#!/bin/bash -e

-E гарантирует, что скрипт выйдет в тот момент, когда что-то вернет false. Определенного сбоя можно избежать с помощьюsomething || true

aggregate1166877
источник