Як вийти, якщо команда не вдалася?


222

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

my_command && (echo 'my_command failed; exit)

але це не працює. Він продовжує виконувати інструкції, наступні за цим рядком у сценарії. Я використовую Ubuntu та bash.


5
Чи планували ви, що незакрита цитата - це синтаксична помилка, яка спричинить збій / вихід? якщо ні, то слід закрити цитату у своєму прикладі.
варильні панелі

Відповіді:


406

Спробуйте:

my_command || { echo 'my_command failed' ; exit 1; }

Чотири зміни:

  • Змінити &&на||
  • Використання { }замість( )
  • Ввести ;після exitі
  • пробіли після {і перед}

Оскільки ви хочете роздрукувати повідомлення та вийти лише тоді, коли команда завершиться невдало (виходить із ненульовим значенням), вам потрібно ||не "an" &&.

cmd1 && cmd2

запуститься, cmd2коли це cmd1вдасться (значення виходу 0). Де як

cmd1 || cmd2

запуститься, cmd2коли cmd1не вдасться (значення виходу не нульове).

Використання ( )змушує команду, що знаходиться всередині них, запускається в підколонці, а виклик exitзвідти змушує вас вийти з підкошти, а не оригінальної оболонки, отже, виконання триває у вихідній оболонці.

Щоб подолати це використання { }

Останні дві зміни потрібні bash.


4
Це, здається, "перевернуто". Якщо функція "вдається", вона повертає 0, а якщо вона "провалюється", вона повертається не нульовою, тому &&, можливо, слід оцінити, коли перша половина повернулася не-нульовою. Це не означає, що відповідь, вказана вище, є невірною - ні, вона є правильною. && і || в сценаріях працюють на основі успіху, а не на поверненому значенні.
CashCow

7
Це здається зворотним, але прочитайте його і має сенс: "виконайте цю команду (успішно)" АБО "надрукуйте цю помилку та вийдіть"
simpleuser

2
Логіка цього полягає в тому, що мова використовує оцінку короткого замикання (SCE). Що стосується SCE, f вираз має вигляд "p OR q", і p оцінюється як істинне, тоді немає жодної причини навіть дивитися на q. Якщо вираз має форму "p AND q", а p оцінюється як помилковий, немає ніяких підстав дивитися на q. Причина цього дворазова: 1) її швидше, з очевидних причин і 2) вона дозволяє уникнути певних помилок (наприклад: "якщо x! = 0 AND 10 / x> 5" вийде з ладу, якщо немає SCE ). Можливість використовувати його в командному рядку, як це, є щасливим побічним ефектом.
користувач2635263

2
Я би рекомендував { (>&2 echo 'my_command failed') ; exit 1; }
повторювати

127

Інші відповіді добре висвітлювали пряме запитання, але ви також можете бути зацікавлені у використанні set -e. При цьому будь-яка команда, яка не працює (за межами конкретних контекстів, таких як ifтести), спричинить скасування сценарію. Для певних сценаріїв це дуже корисно.


3
На жаль, це також дуже небезпечно - set -eмає довгий і прихований набір правил про те, коли це працює, а коли - ні. (Якщо хтось біжить if yourfunction; then ..., він set -eніколи не запускатиме всередину yourfunctionабо що-небудь, на що дзвонить, оскільки цей код вважається "перевіреним"). Дивіться розділ вправ BashFAQ # 105 , де обговорюється ця та інші підводні камені (деякі з яких стосуються лише конкретних версій оболонок, тому вам потрібно обов’язково перевірити кожен реліз, який може бути використаний для запуску вашого сценарію).
Чарльз Даффі

67

Зауважте також, що статус виходу кожної команди зберігається в змінній оболонки $?, Що ви можете перевірити відразу після запуску команди. Ненульовий статус вказує на збій:

my_command
if [ $? -eq 0 ]
then
    echo "it worked"
else
    echo "it failed"
fi

10
Це можна просто замінити, якщо my_command, тут не потрібно використовувати тестову команду.
Барт Сас

5
+1, тому що я думаю, що ви не повинні бути покарані за те, що зазначаєте цю альтернативу - це має бути на столі - хоча це якось некрасиво і, на мій досвід, занадто просто запускати іншу команду між ними, не помічаючи природи тесту (можливо, я ' м просто дурний).
Тоні Делрой

1
@BartSas Якщо команда довга, краще поставити її у свій рядок. Це робить сценарій більш читабельним.
Майкл

@Michael, не якщо це створює залежності по лініях, де вам потрібно знати, що сталося по лінії А, перш ніж ви зможете зрозуміти лінію B (або, що ще гірше, потрібно знати, що буде в рядку B, перш ніж ви знаєте, що безпечно щось додати новий після закінчення рядка А). Я бачив занадто багато разів, коли хтось додав echo "Just finished step A", не знаючи, що це echoвикреслить те, $?що буде перевірено пізніше.
Чарльз Даффі

67

Якщо ви хочете такої поведінки для всіх команд у вашому сценарії, просто додайте

  set -e 
  set -o pipefail

на початку сценарію. Ця пара варіантів пропонує інтерпретатору bash виходити, коли команда повертається з ненульовим кодом виходу.

Однак це не дозволяє надрукувати вихідне повідомлення.


8
Ви можете запускати команди при виході за допомогою trapвбудованої команди bash.
Гевін Сміт

Це відповідь на питання XY.
jwg

5
Можливо, буде цікаво пояснити, що команди виконують самостійно, а не пару. Тим більше, що set -o pipefailможе не бути бажаної поведінки. Все ж дякую, що вказали на це!
Антуан Пінсар

3
set -eзовсім не є надійним, послідовним чи передбачуваним! Щоб переконатись у цьому, перегляньте розділ з вправами BashFAQ № 105 або таблицю, де порівнюються поведінки різних снарядів на in-ulm.de/~mascheck/various/set-e
Чарльз Даффі,

14

Я зламав таку фразу:

echo "Generating from IDL..."
idlj -fclient -td java/src echo.idl
if [ $? -ne 0 ]; then { echo "Failed, aborting." ; exit 1; } fi

echo "Compiling classes..."
javac *java
if [ $? -ne 0 ]; then { echo "Failed, aborting." ; exit 1; } fi

echo "Done."

Попереднюйте кожну команду з інформативним відлунням і виконайте кожну команду в тому ж
if [ $? -ne 0 ];...рядку. (Звичайно, ви можете відредагувати це повідомлення про помилку, якщо хочете.)


Це можна визначити як функцію, таким чином повторно використовувати її.
Ерік Ван

1
'якщо [$? -не 0]; тоді ... fi '- складний спосіб написання' || '
Кевін Клайн

if ! javac *.java; then ...- навіщо взагалі турбуватися $??
Чарльз Даффі

Чарльз - тому що я посередній баскетболіст у кращому випадку :)
Грант

10

За умови my_command , що канонічно розроблено, тобто повертає 0, коли це досягає успіху, то &&це якраз протилежне тому, що ви хочете. Ти хочеш ||.

Також зауважте, що (це не здається мені правильним у баші, але я не можу спробувати, де я є. Скажи мені.

my_command || {
    echo 'my_command failed' ;
    exit 1; 
}

IIRC, тобі потрібні крапки з комою після кожного з рядків всередині {}
Алекс Хованський

9
@Alex: ні, якщо вони знаходяться в окремому рядку.
Призупинено до подальшого повідомлення.

5

Ви також можете використовувати, якщо ви хочете зберегти статус помилки виходу, і мати читабельний файл з однією командою на рядок:

my_command1 || exit $?
my_command2 || exit $?

Це, однак, не надрукує додаткового повідомлення про помилку. Але в деяких випадках помилка буде все-таки надрукована помилковою командою.


1
Причин для цього немає exit $?; просто exitвикористовує $?як значення за замовчуванням.
Чарльз Даффі

@CharlesDuffy не знав. Дивовижно, так це ще простіше!
алекспірин

3

trapВбудована оболонка дозволяє ловити сигнали, а також інші корисні умови, в тому числі не вдалося виконання команди (тобто статус повернення не дорівнює нулю). Отже, якщо ви не хочете явно перевіряти стан повернення кожної окремої команди, ви можете сказати, trap "your shell code" ERRі код оболонки буде виконуватися будь-коли, коли команда поверне ненульовий статус. Наприклад:

trap "echo script failed; exit 1" ERR

Зауважте, що, як і в інших випадках лову невдалих команд, трубопроводи потребують спеціального лікування; вищезгадане не застане false | true.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.