Сохранить код выхода на потом

15

Итак, у меня есть небольшой скрипт для запуска некоторых тестов.

javac *.java && java -ea Test
rm -f *.class

Теперь проблема в том, что когда я запускаю скрипт ./test, он вернет код успешного завершения, даже если тест не пройден из-за rm -f *.classуспеха.

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

javac *.java && java -ea Test
test_exit_code=$?
rm -f *.class
if [ "$test_exit_code" != 0 ] ; then false; fi

Но это похоже на обычную проблему - выполнить задачу, очистить, а затем вернуть код завершения исходной задачи.

Какой самый идиоматичный способ сделать это (в bash или просто в оболочках в целом)?

math4tots
источник

Ответы:

5

Вы можете обернуть exitи rmкоманды в единую простой команды с evalкак:

java ... && java ...
eval "rm -f *.class; exit $?"

$?Значение этого пути при передаче exitявляется тем, что назначено непосредственно перед evalзапуском.

mikeserv
источник
evalвсегда любимый фанатом.
mikeserv
23

Я бы пошел с:

javac *.java && java -ea Test
test_exit_code=$?
rm -f *.class
exit "$test_exit_code"

Зачем прыгать, когда exitесть в наличии?


Вы можете использовать trap:

trap 'last_error_code=$?' ERR

Например:

$ trap 'last_error_code=$?' ERR
$ false
$ echo $?
1
$ echo $last_error_code $?
1 0
Мур
источник
Ах, я согласен, что это лучше, чем мой оригинал. Но я все еще чувствую себя неудовлетворительно, что мне приходится явно хранить код выхода в переменной. Разве нет способа «нажать» код выхода и снова «выкинуть» его позже?
math4tots
@ math4tots Попробуйте обновить.
Муру
Так что с вашим обновлением мне придется инициализировать last_error_code равным нулю, а затем возвращать в конце, чтобы у меня был ненулевой код выхода, если какая-либо команда вызвала ошибку? Это крутой трюк, но для моего двухстрочного сценария взлома, я думаю, я предпочитаю ответ @mikeserv.
math4tots
@ math4tots Вы всегда можете сделать exit ${last_error_code:=0}.
Муру
@avip для чего? Он уже в одинарных кавычках, поэтому переменная оценивается только при вызове ловушки.
Муру
9

Насколько я знаю, самое близкое, что есть у bash к try...finallyблоку из более C-подобного языка программирования (что вы, вероятно, хотели бы получить, если бы он был доступен), - это trapконструкция, которая работает следующим образом:

trap "rm -f *.class" EXIT
javac *.java && java -ea Test

Это выполнит "rm -f * .class", когда ваш скрипт завершится. Если у вас есть что-то более сложное, вы можете поместить это в функцию:

cleanup() {
    ...
}
trap cleanup EXIT
javac *.java && java -ea Test

Если вы так склонны, вы можете превратить это в довольно общую идиому, которая работает примерно как try...catch...finallyблок в C. Что-то вроде этого:

(
  trap "catch_block; exit" ERR
  trap finally_block EXIT
  # contents of try goes here
)

Обратите внимание, что скобки разделяют подоболочку; при такой конструкции в случае сбоя команды завершается только подоболочка, а не весь скрипт. Помните, что подоболочки являются в некотором смысле вычислительно дорогими, поэтому не используйте их слишком много (сотни). В зависимости от вашего сценария вы можете добиться того же эффекта более эффективно с помощью функций оболочки и trap ... RETURN, но это ваше дело исследовать.

Дэвид З
источник