1

How can I do, in a Bash script, to handle the return value of an internal function (which may return a non-zero value) without to be caught by the trap?

For instance, if the perform_test returns 1 the script is ended because the non-zero return value is trapped and handled by the call of the exit_handler function.

How can I do to avoid this behaviour?

Thanks

Here the script:

#!/bin/bash

set -o pipefail  # trace ERR through pipes
set -o errtrace  # trace ERR through 'time command' and other functions
set -o nounset   ## set -u : exit the script if you try to use an uninitialised variable
set -o errexit   ## set -e : exit the script if any statement returns a non-true return value

exit_handler(){
    #...
    echo "${error_filename}${error_lineno:+: $error_lineno}: ${error_msg}"
    exit "${error_code}"
}

trap exit_handler EXIT      # ! ! ! TRAP EXIT ! ! !
trap exit ERR               # ! ! ! TRAP ERR  ! ! !


perform_test(){
    local resultCall=$(...)

    if [[ -n ${resultCall} ]]; then
        return 0
    else
        return 1
    fi
}


##---------------- MAIN ----------------

perform_test
if [[ $? -eq 0 ]]; then
    #...
fi

#...

exit 0

UPDATE:

According to the @choroba's answer (use if perform_test "sthg" ; then), the return 1 isn't caught by the trap as I expected.

But unfortunately this solution is incomplete for my use case: if the function perform_test produce an error (ex: command not found, no such file..., etc.), then this error isn't caught by the trap any more & the script doesn't directly stop...

So, how do that "caught error without catching return 1"?

Here is a working example for illustrating it:

#!/bin/bash

set -o pipefail  # trace ERR through pipes
set -o errtrace  # trace ERR through 'time command' and other functions
set -o nounset   ## set -u : exit the script if you try to use an uninitialised variable
set -o errexit   ## set -e : exit the script if any statement returns a non-true return value

exit_handler (){
    error_code=$?
    if [[ ${error_code} -eq 0 ]]; then
        return; 
    fi

    echo "an error has occurred..."
    exit "${error_code}"
}

trap exit_handler EXIT      # ! ! ! TRAP EXIT ! ! !
trap exit ERR           # ! ! ! TRAP ERR  ! ! !


perform_test(){
    local resultCall=$1

    # file.txt doesn't exist
    cat file.txt

    if [[ -n ${resultCall} ]]; then
        echo ">> the variable is non empty"
        return 1
    else
        echo ">> the variable is empty"
        return 0
    fi
}


##---------------- MAIN ----------------

echo "first test"

if perform_test "sthg" ; then
    echo ">test1 has succeed"
else
    echo ">test1 has failed"
fi

echo "second test"

perform_test "sthg"
if [[ $? -eq 0 ]] ; then
    echo ">test2 has succeed"
else
    echo ">test2 has failed"
fi

echo "end"


trap - EXIT ERR

exit 0

Produce the following output:

first test
cat: file.txt: No such file or directory
>> the variable is non empty
>test1 has failed

second test
cat: file.txt: No such file or directory
an error has occurred...
Jolta
  • 2,620
  • 1
  • 29
  • 42
gudepier
  • 3,362
  • 6
  • 22
  • 26
  • 1
    There are some people who claim that [`set -e` is not recommended](http://mywiki.wooledge.org/BashFAQ/105) in scripts. Please have a look at their arguments and decide by yourself. – gniourf_gniourf Jun 25 '14 at 13:14
  • Sure its not just calling it at the end when you `exit 0` as it will skip the if statement ? Guessing it still does it if you just comment out everything after `##---------------- MAIN ----------------` –  Jun 25 '14 at 13:23
  • Trapping the exit and err handler is really a very lazy way of scripting. Trying to implement `try - catch` mechanisms in Bash is really never going to work. You don't even get to know the specific cause of the error. The only way always is to check the error codes of every command you run - and give them specific messages based on their own error. – konsolebox Jun 25 '14 at 13:25
  • @gniourf_gniourf: I was obliged to add the `set -e` to detect any internal error if others tools aren't correctly installed (ie: Maven)... – gudepier Jun 25 '14 at 13:57
  • @konsolebox: checking every commands will be very heavy and ugly -_- But be assured I've developed a function to display the last error code/message (I get the inspiration on [this post](https://stackoverflow.com/questions/64786/error-handling-in-bash)). – gudepier Jun 25 '14 at 14:12
  • @gudepier It's even uglier using such complex methods. Won't you still give explicit messages for every ERR trap that may happen? Isn't that still heavy? Also, I believe ERR messages that may be missed by the usual methods are more likely not for the end-user but for the script developer or debugger. If you receive such error messages, probably your script is still not well-made. – konsolebox Jun 27 '14 at 08:53

2 Answers2

2

Don't run the function on itself, run it directly in the if condition:

if perform_test ; then
    ...
fi

Also, before you finally exit, you should clear the trap.

trap '' EXIT
choroba
  • 231,213
  • 25
  • 204
  • 289
  • The reset of the `trap - ERR` is done in a cleanup function called before my finally exit, I omitted to write it for my issue ;-) Moreover, can you tell me what is the difference between "`if [[ perform_test ]]; then`" and "`if perform_test ; then`" (respectively the first one doesn't print `echo`s & doesn't work and the second works) ? - thks – gudepier Jun 25 '14 at 15:13
  • @gudepier: `[[ perform_test ]]` is equivalent to `[[ -n perform_test ]]`, so it always returns true, as "perform_test" is a non-empty string. Using `if perform_test` runs perform_test and the if branch is chosen based on its exit status. – choroba Jun 25 '14 at 16:15
2

Do

your_command args || {
    echo "Failed to xyz." >&2
    exit 1
}

or

your_command args
if <test $?>; then
...
[elif ...
...
fi]

or

your_command args
case "$?" in
...
esac

You can also use a function:

function error-exit {
    echo "$1" >&2
    exit 1
}
konsolebox
  • 72,135
  • 12
  • 99
  • 105