set -e и короткие тесты - PullRequest
       39

set -e и короткие тесты

17 голосов
/ 03 августа 2011

Когда я был новичком в сценариях оболочки, я использовал множество коротких тестов вместо if операторов, таких как false && true.

Затем позже я научился использовать set -e и обнаружил, что мои сценарии былиумер по какой-то причине, и они сработали бы, если бы я заменил короткие тесты полными if утверждениями.Теперь время прошло, и я все еще использую только полные операторы if.

Самое интересное, что если я открою интерактивную оболочку и сделаю следующее:

set -e
false && true
echo $?

возвращает 1, но оболочка не умирает!

Я вижу, что я трачу слишком много строк кода.Любой может объяснить мне, как я могу безопасно использовать set -e с короткими тестами, например.без сценария умирает?

Ответы [ 4 ]

36 голосов
/ 03 августа 2011

Спецификация Single UNIX описывает эффект set -e как:

Когда эта опция включена, если простая команда завершается неудачно по любой из причин, перечисленных в разделе Последствияошибок оболочки или возвращает значение состояния выхода> 0, и не является частью составного списка через некоторое время, до или после ключевого слова if, и не является частью списка AND или OR, и не является конвейером, которому предшествует!зарезервированное слово, то оболочка должна немедленно выйти.

Как видите, сбойная команда в списке AND не приведет к завершению оболочки.

Использование set -e

Запуск сценариев оболочки с set -e считается наилучшей практикой, поскольку обычно безопаснее прервать выполнение сценария в случае возникновения ошибки.Если команда может быть безобидной, она обычно добавляется к ней || true.

Вот простой пример:

#!/bin/sh
set -e

# [...]
# remove old backup files; do not fail if none exist
rm *~ *.bak || true
5 голосов
/ 03 августа 2011

Вам необходимо завершить каждую команду, которая может завершиться с ошибкой ||, и список команд или команд, имеющий значение 0. Использование || вызовет вашу команду, если выражение перед оператором не оценивается как 0. Ваша команда должна оценить до 0, чтобы не убить оболочку.

Пример:

set -e
false || true # silently ignore error

false || echo "Command failed, but exit status of echo is 0. Continuing..."

false || {                                                                      
  echo "Command failed. Continuing..."                                          
  # do something else                                                           
  false && true # all commands in the list need to be true                      
}

false && true не вызывает выхода, поскольку конечное выражение оценивается и оценивается как 0. Со страницы руководства bash для set -e:

Выйти немедленно, если конвейер (который может состоять из одного простая команда), команда subshell, заключенная в скобки, или одна из команды, выполняемые как часть списка команд, заключенного в фигурные скобки (см. ОБРАЗЕЦ ОБОЛОЧКИ выше) выходит с ненулевым статусом. Оболочка не завершается, если сбойная команда является частью списка команд сразу после ключевого слова или до, часть теста после зарезервированных слов if или elif, часть любой выполненной команды в && или || список, кроме команды, следующей за последним символом && или || , любая команда в конвейере, кроме последней, или если команда возвращается значение инвертируется с! Ловушка ERR, если установлена, выполняется до выхода оболочки. Этот параметр применяется к среде оболочки и каждая оболочка в отдельности (см. КОМАНДНОЕ ИСПОЛНЕНИЕ ОКРУЖАЮЩАЯ СРЕДА выше), и может привести к выходу подоболочек перед выполнением все команды в подоболочке.

Только последняя команда, если она выполнена в цепочке || / &&, может вызвать выход.

Следующее выражение не будет выполнено по вышеуказанной причине.

true && false

но следующее не будет:

if true && false                                                                
then                                                                            
  true                                                                          
fi                   

из-за true && false является частью теста, следующего за if.

2 голосов
/ 22 июля 2015

Что касается моей проблемы с неожиданно умирающим сценарием, это связано с выполнением этих коротких тестов в подоболочке или из функции, и указанная функция возвращает значение, отличное от нуля:

Исходный пример:

$ set -e
$ false && true
$ echo $?
1

Использование подоболочки или функции:

$ set -e
$ (false && true)
<script died>

$ set -e
$ variable=$(false && true)
<script died>

$ set -e
$ foo()(false && true)
$ foo
<script died>

$ set -e
$ foo(){ false && true; }
$ foo
<script died>

Возможные решения:

$ set -e
$ (false && true ||:)
$ (false && true) ||:
$ (if false; then true; fi)
1 голос
/ 04 августа 2011

Поведение set -e не интуитивно понятно во всех случаях, кроме самых простых, и с годами оно менялось несколько раз.Поэтому я не рекомендую использовать его, за исключением действительно простых сценариев (простых последовательностей команд).

Обратите внимание, что команды в Makefile обычно выполняются с действующим set -e, поэтому все еще часто необходимо что-то пониматьо том, как это работает.

Операторы && и || допускают аналогичный эффект с относительно небольшим беспорядком, но делают его явным.В частности, && может быть помещен в конце строки, очень похожий на `; '.

...