如何在bash中创建RETURN陷阱保留返回码?

tij*_*agi 6 linux bash bash-trap

下面是我正在编写的脚本的简化方案.程序必须以不同的方式获取参数,因此可以对多个函数进行精细划分.

问题是来自深层函数的返回值的链式加载在陷阱上中断,其中要检查结果以显示消息.

#! /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’.
Run Code Online (Sandbox Code Playgroud)

它应该如何工作:

              ? 3 ? 3? trap ?3                     ? || ask_for_params ?
 check_a_param >>> check_params >>> [ ! -v MANUAL ]                     ?
              ? 0 ? 0? trap ?0                     ? && ____________ do_the_job
Run Code Online (Sandbox Code Playgroud)

这个想法是,如果检查失败,它的返回代码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
Run Code Online (Sandbox Code Playgroud)

如果您尝试按原样运行脚本,您应该看到

Bad, bad… Dropping to manual setup.
check_params() returned with 0. Not running manual setup.
Run Code Online (Sandbox Code Playgroud)

这意味着坏结果触发了陷阱(!)但是设置它的母函数没有传递结果.

试图设置一个我试过的黑客攻击

  • 设置RETVAL作为全局变量declare -g retval=$?return_trap()和在该行设置陷阱使用它的值.变量已设置([ -v retval ]成功返回),但......没有值.滑稽.
  • 好吧,让我们把retval=Eehcheck_params(),外面的return_trap(),只是将其设置为$?一个平常PARAM.不,retval函数中没有设置全局变量的值,它保持'Eeh'.不,没有local指令.默认情况下应将其视为全局.如果你把test=1check_params()test=3check_a_param(),然后用它打印echo $test在年底setup(),你应该看到3.至少我是这样.declare -g正如预期的那样,这里没有任何区别.
  • 也许这是功能的范围?不,那也不是.return_trap()一起移动declare -g retval=Eeh没有任何区别.
  • 当现代软件意味着堕落时,是时候采用好的旧文字写入文件了.让我们用retval=$?; echo $retval >/tmp/tin 打印retval到/ tmp/t return_trap()并用它读回

    trap "return_trap; trap - RETURN; return $(</tmp/t)" 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.
Run Code Online (Sandbox Code Playgroud)

如果trap命令的参数严格地是函数名,则返回原始结果.原来的,不是什么return_trap()回报.我试图增加结果但仍然得到3.你也可能会问'为什么你需要如此疏远陷阱呢?'.这是为了避免另一个错误,即每次check_params()从另一个函数调用时,它会导致陷阱每次都触发.RETURN上的陷阱是本地的东西,除非显式设置了调试或跟踪标志,否则它们不会被其他函数继承,但看起来它们在运行之间会在它们上设置陷阱.或者bash为他们保留陷阱.只有在从特定函数调用check_params()时才应设置此陷阱,但如果未设置陷阱,则每次check_a_param()返回大于零的值时都会继续触发,而不依赖于FUNCNAME[1].

在这里我放弃了,因为我现在看到的唯一一个出口就是在每次|| return $?进入之前对调用函数进行检查check_params().但它太丑了,它伤害了我的眼睛.

我可以只添加,$?在该行设置的陷阱始终返回0.所以,如果你,例如,声明一个local变量retvalreturn_trap(),并把这样的代码来检查它

trap "return_trap; [ -v retval ]; echo $?; trap - RETURN; return $retval" RETURN
Run Code Online (Sandbox Code Playgroud)

它将打印0,无论是否retval实际设置,但如果您使用

trap "return_trap; [ -v retval ] && echo set || echo unset; trap - RETURN; return $retval" RETURN
Run Code Online (Sandbox Code Playgroud)

它会打印'未设置'.


GNU bash,版本4.3.39(1)-release(x86_64-pc-linux-gnu)

tij*_*agi 5

好笑的,

trap "return_trap; trap - RETURN" RETURN
Run Code Online (Sandbox Code Playgroud)

只是工作.

[ ! -v MANUAL ] && {
    check_params; retval2=$?
    [ $retval2 -eq 0 ] \
        && echo "check_params() returned with 0. Not running manual setup." \
        || false
}|| ask_for_params
Run Code Online (Sandbox Code Playgroud)

这是追踪.

+ 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.
Run Code Online (Sandbox Code Playgroud)

所以答案很简单:不要试图覆盖传递给trap命令的行中的结果.Bash为您处理一切.