Search code examples
bashreturnexitterminate

How to break out of a sourced Bash script's function


I have a Bash script that is sourced. When this script is sourced, it runs a function in the Bash script. This function should terminate the script if a certain condition is matched. How can this be done without terminating the shell in which the script is sourced?

To be clear: I want the termination action to be completed by the function in the sourced shell script, not in the main body of the sourced shell script. The problems that I can see are that return simply returns from the function to the main of the script while exit 1 terminated the calling shell.

The following minimal example illustrates the problem:

main(){
    echo "starting test of environment..."
    ensure_environment
    echo "environment safe -- starting other procedures..."
}

ensure_environment(){
    if [ 1 == 1 ]; then
        echo "environment problemm -- terminating..."
        # exit 1 # <-- terminates calling shell
        return   # <-- returns only from function, not from sourced script
    fi
}

main

Solution

  • This is a recipe how you can achieve your goal with your approach. I will not write your code for you, just describe how it can be done.

    Your goal is to set/alter environment variables in the current bash shell by, effectively, sourcing a possibly complex shell script. Some component of this script may decide that execution of this sourced script should stop. What makes this complicated is that this decision is not necessarily top-level, but may be located in a nested function invocation. return, then, does not help, and exit would terminate the sourcing shell, which is not desired.

    Your task is made easier by this statement of yours:

    additional complexity that I can't really include in a minimal example makes it very desirable to centralise the termination procedure in a function.

    This is how you do it:

    Instead of sourcing your real script that decides which environment to set to what ("realscript.bash"), you source another script "ipcscript.bash".

    ipcscript.bash will setup some interprocess communication. This may be a pipe on some extra file descriptor that you open with exec, it may be a temporary file, it may be something else.

    ipcscript.bash will then start realscript.bash as a child process. That means, the environment changes that realscript.bash does first only affect the environment of that child process instance of bash. Starting realscript.bash as a childprocess, you gain the capability of terminating the execution at any nested level with exit without terminating the sourcing shell.

    Your call to exit will live, as you write, in a centralised function that is called from any level when a decision is made to terminate execution. Your terminating function now needs, before exiting, to write the current environment to the IPC mechanism in a suitable format.

    ipcscript.bash will read environment settings from the IPC mechanism and reproduce all settings in the process of the sourcing shell.