LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (http://www.linuxquestions.org/questions/programming-9/)
-   -   Exiting BASH function without exiting sourced environment (http://www.linuxquestions.org/questions/programming-9/exiting-bash-function-without-exiting-sourced-environment-676221/)

SwingingSimian 10-14-2008 05:30 AM

Exiting BASH function without exiting sourced environment
 
Hi all

I'm writing an environment in BASH and want to be able to exit from a function without exiting from the whole environment.

For example:

>. my_env.sh

#This sets PS1 to my_env and contains a function called RunExit, which simply exits. The idea is that this will be called by other functions when an error is encountered, but we simply want to stop running the function, rather than exiting the environment


This is what happens with a simple exit command

my_env>RunExit
>

Where as I want to be able just to exit the function, so that we still have access to the environment and all of it's config & functions e.g.

my_env>RunExit
my_env>


I'm currently trapping all EXIT commands with a function which simply calls bash just before it ends, so it exits from the subshell rather than from the environment.

################################################################################
# Func : ReportExit()
# Desc : Reports exit status but calls bash before exit, so we don't exit the environment
# Args [1] :
# Return :
# Exception :
################################################################################

ReportExit(){
es=$?
echo "Exiting with status:$es"
bash
}



This seems to work okay. But this doesn't work all the time and I end up with several instances of bash running. I guess there is a cleaner way of doing this, but I can't find anything obvious at the moment.

Any suggestions would be greatly appreciated.

Thanks

Nath

mudflap 10-14-2008 07:21 AM

>> SwingingSimian

I am not sure if I understand what you are doing, but I will jump in anyway.

Will multiple terminals solve your problem? Each terminal should maintain an independant environment.

It sounds like a "chroot" might be a better approach.

SwingingSimian 10-14-2008 10:41 AM

No I don't think so, I want all this running via one terminal.

The enviroment is supposed to set up some config when sourced and then return you to the command line where you can execute any number of interactive functions which were defined in the environment.

What I want is a way to exit from a function without exiting the environment. Return doesn't work as this simply returns to the caller function, which would mean I would have to do some sort of mammoth return value tracking right back to the function originally called.

mudflap 10-15-2008 07:17 AM

>> SwingingSimian


How about:

---
ding_bell() {
echo -e "\a"
}

alias exit="ding_bell"
---

"unalias exit" will restore exit to expected behaviour, no need then to invoke another instance of bash. The environment remains unique to that terminal.

chrism01 10-15-2008 07:10 PM

Here's another way of changing what exit does: http://www.tldp.org/LDP/abs/html/functions.html
Look at the NO_EXIT example.
Or you can use the 'return' cmd from within a fn: http://www.faqs.org/docs/abs/HTML/functions.html

SwingingSimian 10-16-2008 04:47 AM

Thanks guys, but aliasing exit simply returns me to the caller of exit after the aliased function has finished, hence the rest of the caller is executed and not really exited. Obviously return does the same
, which is what I want to avoid.


I actually want the script to stop running from any point in a series of potentially nested functions, but without exiting the environment which was sourced.

e.g.

The methods are sourced on the command line from an environment file

Func1(){

Func2

Func3
}

Func2(){

exit
}


Aliasing or returning would from Func2 simply returns to Func1 and Func3 would be executed, which is what I want to avoid. Normal exit behaviour exits the environment totally an none of the above Funcs or associated environment are available.

Nath

Mr. C. 10-16-2008 05:09 AM

You'll have to test the return values of your functions to determine if you want to continue execution, or return up the stack to unwind (this is like setjump/longjmp).

SwingingSimian 10-16-2008 05:15 AM

Ah, this is what I feared, so I will have to implement return value tracking through all of my functions. I'll take a look setjump/longjmp to see whether they have any neat solutions.

Thanks

Nath

kdogksu 08-11-2009 03:12 PM

Sorry for a late response, but this may be helpful to others.

I'm a little confused also about what you're trying to do. The first post sounds like you want "exit" in a function to only return from the function and not exit the environment, and in the last post, it sounds like you wanted it to exit completely so that you didn't have to trace the return values.

Regardless, you may want to look into the "set -e" option of Bash. This will exit a script when any shell command returns a non-zero status. If you want your function to exit the entire script without having to track return values, you can "set -e" in the caller, then "return 1" from the function to exit the whole script/environment. ("return 0" continues execution normally.) If you only want this for certain areas of your script, you can later call "set +e" to disable this option.

In one of my scripts, I wanted a function to collect the output of shell commands, but not print it unless there was an error. This keeps the user from seeing all the messy output of verbose commands, but allows it to be displayed if something goes wrong. I had trouble because "set -e" was set in my environment to quit if the function returned "1", but this caused the shell to exit after the failed shell command itself, not waiting until the wrapper function exited. I fixed this by querying the status of "-e", disabling it in the function, then restoring it after running the command but before returning from the function. This function is included below as an example. (I'm not a great shell scripter, so neither quality nor efficiency is guaranteed. :) )

Ultimately, it would be nice if "exit" exited a whole environment, while "return" only exited a function as it is in C. However, this doesn't seem to be the case.

Code:

# Runs a command, collecting output in a file.  The output is
# only displayed if the command returns a non-zero status.
run_clean() {
    local output_file="$(mktemp /tmp/output-XXXXXX)"
    local die="no"
    local autoexit="off"

    # Query status of "-e", disable if set
    if echo ${-} | grep "e" > /dev/null; then
        autoexit="on"
        set +e
    fi

    # Run command and display output if return value is non-zero
    ${*} > ${output_file} 2>&1
    if [ ${?} -ne 0 ]; then
        cat ${output_file}
        die="yes"
    fi

    # Restore "-e"
    if [ "${autoexit}" == "on" ]; then
        set -e
    fi

    # Cleanup
    rm ${output_file}
    if [ "${die}" == "yes" ]; then
        exit 1;
    fi

    return 0
}


catkin 08-12-2009 12:50 AM

Quote:

Originally Posted by SwingingSimian (Post 3311979)
Ah, this is what I feared, so I will have to implement return value tracking through all of my functions. I'll take a look setjump/longjmp to see whether they have any neat solutions.

setjump/longjmp are C facilities, not bash facilities. I don't think the solution you are looking for exists. What do you want to achieve? There may be another way.

RaptorX 08-12-2009 12:59 AM

At first I also thought of "return" but as kdogksu mentioned it does not work as in c.
I am not a bash programmer (just a beginner) but I believe that if you "^C" a function it will stop just the function itself, now I do not know if you can pass that in to a script.

EDIT:

according to this script you can:

Quote:

TARGETFILE=$1

# Insert 2 lines in file, then save.
#--------Begin here document-----------#
vi $TARGETFILE <<x23LimitStringx23
i
This is line 1 of the example file.
This is line 2 of the example file.
^[
ZZ
x23LimitStringx23
#----------End here document-----------#

# Note that ^[ above is a literal escape
#+ typed by Control-V <Esc>.
So you can give it a try to see what happens.

snipped from Advanced Bash Scripting tutorial.
http://tldp.org/LDP/abs/html/here-docs.html#HEREDOCREF


All times are GMT -5. The time now is 03:58 AM.