Bash trap on exit from function
Asked Answered
E

3

36

Is there possible in bash to call some command when function exits. I mean something like:

function foo
{
    # something like this maybe?
    trap "echo \"exit function foo\"" EXIT

    # do something
}

foo

And i want exit function foo to be printed out.

Eimile answered 24/8, 2017 at 12:46 Comment(1)
I needed this for a different context because my traps were leaking into my normal shell from bash_profile functions and I needed to catch and reset them before function termination. Regardless thankyou thankyou thankyou for asking this!Tristantristas
S
50

Yes, you can trap RETURN :

$ function foo() {
>   trap "echo finished" RETURN
>   echo "doing some things"
> }
$ foo

Will display

doing some things
finished

From man bash's description of the trap builtin :

If a sigspec is RETURN, the command arg is executed each time a shell function or a script executed with the . or source builtins finishes executing.

Spanishamerican answered 24/8, 2017 at 12:52 Comment(11)
And can you trap also return code of this function?Eimile
No; you'll need to use a conditional like if or case if you want to take different actions based on the upcoming return code of the function. Note, too, that trap sets the handler globally, so any trap on RETURN that existed before foo was called is replaced once foo is called (given that the body of foo is a { ... } command; foo () ( trap ...; ) would not affect the calling context).Pannell
That said, you can trap ERR to fire on any non-zero return status in addition to the trap on RETURN. You just can't set different traps for a return code of 1 vs a return code of 2.Pannell
But can i somehow print error code in trap? Something like: trap "echo \"error_code: $?\"" RETURNEimile
@Eimile no, you can't. Even when using an implicit return, $? inside the trap command won't contain the success value of the last command executed in the scope of the function. If what you want is to trace the start/end and result of function executions there are other alternatives to traps thoughSpanishamerican
Can you tell more about this alternatives? I am working on logging component and I want to log when function exits and what was the result. But I want to have control wether this information should be logged or not.Eimile
@Eimile I'm not so confident in my knowledge about them, it might be worth asking another question describing your functional need and explaining how you tried to implement it with traps. An obvious solution would be a wrapper function which takes a function name and arguments as parameters, log them, call the function, log and return the function success value and outputs. That would require using eval if I'm not mistaken, which is usually seen as bad practice. I believe there are other natives solutions, akin bash -x but less verbose / more targeted, but I can't remember any of them.Spanishamerican
Solution with wrapper seems not so bad. Maybe I will try it. ThanksEimile
Use EXIT to call handler when not function but a script exits.Michaella
the returncode of the current function can be found in ${BASH_COMMAND:7} see gnu.org/software/bash/manual/html_node/Bash-Variables.htmlOneal
@IanCarter that can be useful but it's also very brittle, it will only correspond to the exit code when there is an explicit return followed by a single space and a constant number. It's interesting nonetheless that ${BASH_COMMAND} holds the last command executed before triggering the trapSpanishamerican
D
4

Concerning exiting with Ctrl+c (without declaring another function for exiting):

#!/bin/bash
function do_stuff() {
  function do_stuff_end() {
    # the code for exiting the function here
    echo "<the code for exiting the function here>"
    unset -f do_stuff_end
    trap "$trap_sigint" SIGINT
    return
  }
  trap_sigint="$(trap -p SIGINT)"
  trap "do_stuff_end; return" SIGINT
  # the code for the function here
  echo "<the code for the function here>"
  do_stuff_end
}

N.B.: the previous code is "just" working but needs improvement by considering the effects of other signals other than SIGINT

Doubleminded answered 18/8, 2021 at 10:23 Comment(0)
O
0

using a RETURN trap along with $BASH_COMMAND allows some cleanup and logging.

foo(){
    trap '[[ "$BASH_COMMAND" == "return "* ]] && >&2 echo -e "\033[31mfunction $FUNCNAME failed with error-code ${BASH_COMMAND:7}\033[0m"' RETURN

    echo something
    # ...
    return 42
}

foo

however this approach has some downsides (like exposing potential inside functions) - using a subshell with an EXIT-trap in the form of try,catch,finally is cleaner:

foo2() {
    (
        try(){
            echo something
            # ...
            return 42
        }
        catch(){
            errno=$?
            >&2 echo -en "\033[31m$(type -t ${FUNCNAME[1]}) '${FUNCNAME[1]}' failed with error-code $errno\033[2;31m. Trace:"
            for ((i=${#BASH_SOURCE[@]} - 1; i > 0; i-- )); do >&2 echo -n " ➛ ${FUNCNAME[$i]} (${BASH_SOURCE[$i]}:${BASH_LINENO[$i]})"; done
            >&2 echo -e "\033[0m"
        }
        finally(){
            :
        }
        trap finally EXIT; local errno=0; try "$@" || catch; exit $errno
    ) || return $?
}

foo2
Oneal answered 22/5 at 22:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.