Is there a way to write a Bash function which aborts the whole execution, no matter how it is called?

I was using the "exit 1" statement in my Bash functions to terminate the whole script and it worked fine:

function func()
{
   echo "Goodbye"
   exit 1
}
echo "Function call will abort"
func
echo "This will never be printed"

But then I realized that it doesn't do the work when called like:

res=$(func)

I understand that I created a subshell and "exit 1" aborts that subshell and not the primary one....

But is there a way to write a function which aborts the whole execution, no matter how it is called? I just need to get the real return value (echoed by the function).


Solution 1:

What you could do, is register the top level shell for the TERM signal to exit, and then send a TERM to the top level shell:

#!/bin/bash
trap "exit 1" TERM
export TOP_PID=$$

function func()
{
   echo "Goodbye"
   kill -s TERM $TOP_PID
}

echo "Function call will abort"
echo $(func)
echo "This will never be printed"

So, your function sends a TERM signal back to the top level shell, which is caught and handled using the provided command, in this case, "exit 1".

Solution 2:

You can use set -e which exits if a command exits with a non-zero status:

set -e 
func
set +e

Or grab the return value:

(func) || exit $?

Solution 3:

A child process can't force the parent process to close implicitly. You need to use some kind of signaling mechanism. Options might include a special return value, or perhaps sending some signal with kill, something like

function child() {
    local parent_pid="$1"
    local other="$2"
    ...
    if [[ $failed ]]; then
        kill -QUIT "$parent_pid"
    fi
}