Скасування сценарію оболонки, якщо будь-яка команда повертає ненульове значення?


437

У мене є сценарій оболонки Bash, який викликає ряд команд. Я хотів би, щоб сценарій оболонки автоматично вийшов зі значенням повернення 1, якщо будь-яка з команд повертає ненульове значення.

Чи можливо це без явної перевірки результату кожної команди?

напр

dosomething1
if [[ $? -ne 0 ]]; then
    exit 1
fi

dosomething2
if [[ $? -ne 0 ]]; then
    exit 1
fi

8
Крім того set -e, також робити set -u(або set -eu). -uприпиняє ідіотичну поведінку, що приховує помилки, що ви можете отримати доступ до будь-якої неіснуючої змінної та мати порожнє значення, що створюється без діагностики.
Каз

Відповіді:


742

Додайте це до початку сценарію:

set -e

Це призведе до негайного виходу оболонки, якщо проста команда завершиться із ненульовим значенням виходу. Проста команда - це будь-яка команда, яка не є частиною if, while, або до тестування, або частиною && або || список.

Для отримання детальної інформації див. Сторінку bash (1) man у внутрішній команді "set".

Я особисто запускаю майже всі скрипти оболонки з "set -e". Дійсно дратувати, щоб сценарій вперто продовжувався, коли щось не виходить із середини та порушує припущення для решти сценарію.


36
Це спрацювало б, але я люблю використовувати "#! / Usr / bin / env bash", оскільки я часто запускаю bash з іншого місця, ніж / bin. І "#! / Usr / bin / env bash -e" не працює. Крім того, приємно мати місце для зміни, щоб прочитати "set -xe", коли я хочу ввімкнути трасування для налагодження.
Віль Лорікарі

48
Крім того, прапори в рядку shebang ігноруються, якщо сценарій запускається як bash script.sh.
Том Андерсон

27
Лише зауваження: Якщо ви оголошуєте функції всередині bash-скрипту, для функцій потрібно буде встановити -e, передекларовану всередині функції функції, якщо ви хочете розширити цю функціональність.
Джин Кім

8
Крім того, якщо ви надсилаєте свій сценарій, рядок shebang буде невідповідним.

4
@JinKim Це, мабуть, не так у башті 3.2.48. Спробуйте наступне всередині скрипта: set -e; tf() { false; }; tf; echo 'still here'. Навіть не маючи set -eвсередині тіла tf(), страта припиняється. Можливо, ти мав намір сказати, що set -eне успадковується підшарами , що правда.
mklement0

202

Щоб додати до прийнятої відповіді:

Майте на увазі, що set -eіноді цього недостатньо, особливо якщо у вас є труби.

Наприклад, припустимо, у вас є цей сценарій

#!/bin/bash
set -e 
./configure  > configure.log
make

... яка працює, як очікувалося: помилка в configureприпиненні виконання.

Завтра ви зробите, здавалося б, тривіальну зміну:

#!/bin/bash
set -e 
./configure  | tee configure.log
make

... і зараз це не працює. Це пояснюється тут , і передбачено вирішення (лише Bash):

#! / бін / баш
встановити -е 
встановити -o трубопровід

./конфігурація | tee configure.log
зробити

1
Дякуємо, що пояснили важливість того, pipefailщоб піти разом set -o!
Малькольм

83

Заяви if у вашому прикладі не потрібні. Просто роби це так:

dosomething1 || exit 1

Якщо ви скористаєтесь порадою Віль Лаурікарі і використаєте set -eдля деяких команд, вам може знадобитися це:

dosomething || true

|| trueЗроблю команду трубопровід має trueзначення, що повертається , навіть якщо команда не виконується , так що -eопція не буде вбивати скрипт.


1
Мені це подобається. Тим більше, що головна відповідь є орієнтацією на башти (мені зовсім не зрозуміло, чи / в якій мірі це стосується сценаріїв zsh). І я міг би це подивитися, але ваш просто зрозуміліший, бо логіка.
g33kz0r

set -eне є орієнтацією на башти - він підтримується навіть на оригінальній оболонці Борна.
Marcos Vives Del Sol

27

Якщо у вас є очищення, яке потрібно зробити під час виходу, ви також можете скористатися "пасткою" з помилкою псевдосигналу. Це працює так само, як захоплення INT або будь-якого іншого сигналу; bash кидає ERR, якщо будь-яка команда закінчується з ненульовим значенням:

# Create the trap with   
#    trap COMMAND SIGNAME [SIGNAME2 SIGNAME3...]
trap "rm -f /tmp/$MYTMPFILE; exit 1" ERR INT TERM
command1
command2
command3
# Partially turn off the trap.
trap - ERR
# Now a control-C will still cause cleanup, but
# a nonzero exit code won't:
ps aux | grep blahblahblah

Або, особливо якщо ви використовуєте "set -e", ви можете захопити EXIT; ваша пастка буде виконуватися тоді, коли скрипт виходить з будь-якої причини, включаючи нормальний кінець, переривання, вихід, викликаний параметром -e тощо.


12

$?Мінлива рідко. Псевдоідіомаcommand; if [ $? -eq 0 ]; then X; fi завжди слід писати як if command; then X; fi.

Випадки, коли $?це потрібно, це коли потрібно перевірити численні значення:

command
case $? in
  (0) X;;
  (1) Y;;
  (2) Z;;
esac

або коли $?потрібно повторно використовувати або іншим чином маніпулювати:

if command; then
  echo "command successful" >&2
else
  ret=$?
  echo "command failed with exit code $ret" >&2
  exit $ret
fi

3
Чому "завжди слід писати як"? Я маю на увазі, чому " повинно " так бути? Коли команда довга (подумайте, викликаючи GCC з десяток варіантів), тоді набагато зручніше виконати команду, перш ніж перевірити стан повернення.
ysap

Якщо команда занадто довга, її можна розбити, називши її (визначте функцію оболонки).
Марк Едгар

12

Запустіть його з верхівкою -eабо set -eна ній.

Подивіться також set -u.


34
Щоб потенційно врятувати інших, потрібно прочитати help set: -uтрактує посилання на незареєстровані змінні як помилки.
mklement0

1
значить, це set -uабо set -eобидва? @lumpynose
ericn

1
@eric Я вийшов на пенсію кілька років тому. Хоча я любив свою роботу, мій постарелий мозок забув усе. Я напевно здогадуюсь, що ви можете використовувати обох разом; погана формулювання з мого боку; Я повинен був сказати "і / або".
грудочка

3

Вираз, як

dosomething1 && dosomething2 && dosomething3

припинить обробку, коли одна з команд повернеться з ненульовим значенням. Наприклад, наступна команда ніколи не надрукує "виконано":

cat nosuchfile && echo "done"
echo $?
1


-2

просто киньте ще одне для довідки, оскільки було додаткове запитання до введення Марка Едгара, і ось додатковий приклад, який стосується загальної теми:

[[ `cmd` ]] && echo success_else_silence

що те саме, cmd || exit errcodeщо хтось показав.

напр. Я хочу переконатися, що розділ відключений, якщо його встановлено:

[[ `mount | grep /dev/sda1` ]] && umount /dev/sda1 

5
Ні, [[ cmd`]] `- це не те саме. Це помилково, якщо вихідні дані команди порожні, а інакше вірно, незалежно від стану виходу команди.
Жил "ТАК - перестань бути злим"
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.