Как заставить ловушку RETURN в bash сохранить код возврата?
Ниже приведена упрощенная схема сценария, который я пишу. Программа должна принимать параметры по-разному, так что есть несколько функций.
Проблема состоит в том, что загрузка цепочки возвращаемого значения из более глубоких функций прерывается в ловушке, где результат должен быть проверен, чтобы показать сообщение.
#! /usr/bin/env bash
check_a_param() {
[ "$1" = return_ok ] && return 0 || return 3
}
check_params() {
# This trap should catch negative results from the functions
# performing actual checks, like check_a_param() below.
return_trap() {
local retval=$?
[ $retval -ne 0 ] && echo 'Bad, bad… Dropping to manual setup.'
return $retval
}
# check_params can be called from different functions, not only
# setup(). But the other functions don’t care about the return value
# of check_params().
[ "${FUNCNAME[1]}" = setup ] \
&& trap "return_trap; got_retval=$?; trap - RETURN; return $got_retval;" RETURN
check_a_param 'return_bad' || return $?
# …
# Here we check another parameters in the same way.
# …
echo 'Provided parameters are valid.'
return 0 # To be sure.
}
ask_for_params() {
echo 'User sets params manually step by step.'
}
setup() {
[ "$1" = force_manual ] && local MANUAL=t
# If gathered parameters do not pass check_params()
# the script shall resort to asking user for entering them.
[ ! -v MANUAL ] && {
check_params \
&& echo "check_params() returned with 0. Not running manual setup."
|| false
}|| ask_for_params
# do_the_job
}
setup "$@" # Either empty or ‘force_manual’.
Как это должно работать:
↗ 3 → 3→ trap →3 ↗ || ask_for_params ↘
check_a_param >>> check_params >>> [ ! -v MANUAL ] ↓
↘ 0 → 0→ trap →0 ↘ && ____________ do_the_job
Идея в том, что если проверка не пройдена, ее код возврата принудительно check_params()
чтобы вернуть тоже, что, в свою очередь, вызовет || ask_for_params
состояние в setup()
, Но ловушка возвращает 0:
↗ 3 → 3→ trap →0
check_a_param >>> check_params >>> [ ! -v MANUAL ] &&… >>> do_the_job
↘ 0 → 0→ trap →0
Если вы попытаетесь запустить скрипт как есть, вы должны увидеть
Bad, bad… Dropping to manual setup.
check_params() returned with 0. Not running manual setup.
Это означает, что плохой результат вызвал ловушку (!), Но функция-мать, которая его установила, не передала результат.
В попытке установить взломать я пытался
- установить retval как глобальную переменную
declare -g retval=$?
вreturn_trap()
и используйте его значение в строке, устанавливающей ловушку. Переменная установлена ([ -v retval ]
возвращается успешно), но... не имеет значения. Смешной. - ладно давайте
retval=Eeh
кcheck_params()
, внеreturn_trap()
и просто установите его$?
как обычный парам. Нет,retval
в функции не устанавливается значение для глобальной переменной, оно остается "Eeh". Нет, нетlocal
директивы. По умолчанию он должен рассматриваться как глобальный. Если вы положитеtest=1
вcheck_params()
а такжеtest=3
вcheck_a_param()
а затем распечатать его сecho $test
в концеsetup()
, вы должны увидеть 3. По крайней мере, я делаю.declare -g
здесь нет никакой разницы, как ожидалось. - может быть, это сфера функции? Нет, это тоже не так. перемещение
return_trap()
вместе сdeclare -g retval=Eeh
не имеет никакого значения. когда современное программное обеспечение означает падение, пришло время прибегнуть к старой доброй записи в файл. Давайте напечатаем ретваль в /tmp/t с помощью
retval=$?; echo $retval >/tmp/t
вreturn_trap()
и прочитать его обратно сtrap "return_trap; trap - RETURN; return $(</tmp/t)" RETURN
Теперь мы можем наконец увидеть, что последняя директива return, которая читает число из файла, на самом деле возвращает 3. Но check_params()
по-прежнему возвращает 0!
++ trap - RETURN
++ return 3
+ retval2=0
+ echo 'check_params() returned with 0. Not running manual setup.'
check_params() returned with 0. Not running manual setup.
Если аргумент к trap
Команда строго является именем функции, она возвращает исходный результат. Оригинальный, а не то, что return_trap()
возвращается. Я попытался увеличить результат и все еще получил 3. Вы также можете спросить: "Зачем вам нужно так много раз сбрасывать ловушку?". Это чтобы избежать другой ошибки, которая заставляет ловушку срабатывать каждый раз, даже когда check_params()
вызывается из другой функции. Ловушки в RETURN являются локальными вещами, они не наследуются другими функциями, если только для них явно не установлены флаги отладки или трассировки, но похоже, что они держат ловушки, установленные на них между запусками. Или Баш держит ловушки для них. Эта ловушка должна быть установлена только тогда, когда check_params() вызывается из определенной функции, но если ловушка не сброшена, она продолжает срабатывать каждый раз check_a_param()
возвращает значение больше нуля независимо от того, что в FUNCNAME[1]
,
Здесь я сдаюсь, потому что единственный выход, который я вижу сейчас, это реализовать проверку вызывающей функции перед каждым || return $?
в check_params()
, Но это так уродливо, что ранит мои глаза.
Я могу только добавить, что $?
в установке строки ловушка всегда будет возвращать 0. Так что, если вы, например, объявите local
переменная retval
в return_trap()
, и поставить такой код, чтобы проверить это
trap "return_trap; [ -v retval ]; echo $?; trap - RETURN; return $retval" RETURN
он будет печатать 0 независимо от того, retval
на самом деле установлен или нет, но если вы используете
trap "return_trap; [ -v retval ] && echo set || echo unset; trap - RETURN; return $retval" RETURN
Будет напечатано "unset".
GNU bash, версия 4.3.39(1)-релиз (x86_64-pc-linux-gnu)
1 ответ
Достаточно смешно,
trap "return_trap; trap - RETURN" RETURN
просто работает.
[ ! -v MANUAL ] && {
check_params; retval2=$?
[ $retval2 -eq 0 ] \
&& echo "check_params() returned with 0. Not running manual setup." \
|| false
}|| ask_for_params
А вот и след.
+ check_a_parameter return_bad
+ '[' return_bad = return_ok ']'
+ return 3
+ return 3
++ return_trap
++ local retval=3
++ echo 3
++ '[' 3 -ne 0 ']'
++ echo 'Bad, bad… Dropping to manual setup.'
Bad, bad… Dropping to manual setup.
++ return 3
++ trap - RETURN
+ retval2=3
+ '[' 3 -eq 0 ']'
+ false
+ ask_for_params
+ echo 'User sets params manually step by step.'
User sets params manually step by step.
Поэтому ответ прост: не пытайтесь перезаписать результат в строке, переданной trap
команда. Баш обрабатывает все для вас.