19

I know there are several SO questions on exit vs. return in bash scripts (e.g. here).

On this topic, but different from existing questions, I believe, I'd like to know if there is a "best practice" for how to safely implement "early return" from a bash script such that the user's current shell is not exited if they source the script.

Answers such as this seem based on "exit", but if the script is sourced, i.e. run with a "." (dot space) prefix, the script runs in the current shell's context, in which case exit statements have the effect of exiting the current shell. I assume this is an undesirable result because a script doesn't know if it is being sourced or being run in a subshell - if the former, the user may unexpectedly have his shell disappear. Is there a way/best practice for early-returns to not exit the current shell if the caller sources it?

E.g. This script...

#! /usr/bin/bash
# f.sh

func()
{
  return 42
}

func
retVal=$?
if [ "${retVal}" -ne 0 ]; then
  exit "${retVal}"
#  return ${retVal} # Can't do this; I get a "./f.sh: line 13: return: can only `return' from a function or sourced script"
fi

echo "don't wanna reach here"

...runs without killing my current shell if it is run from a subshell...

> ./f.sh 
> 

...but kills my current shell if it is sourced:

> . ./f.sh 

One idea that comes to mind is to nest code within coditionals so that there is no explicit exit statement, but my C/C++ bias makes think of early-returns as aesthetically preferable to nested code. Are there other solutions that are truly "early return"?

13
  • 3
    The question isn't "when", but "how". If the script is sourced, you need to use return. Whether the script should be sourced or executed in its own process is something that should be documented, and IMO there aren't a lot of use cases that require leaving it up to the user to pick one or the other. Commented Aug 24, 2018 at 22:31
  • 2
    No, return can also be used to return from a script that is being sourced. Commented Aug 24, 2018 at 22:34
  • 1
    @StoneThrow, ...in terms of why it's the case -- to be safely sourced, a script needs to be written to not have side effects on the shell that invoked it, and needs to work even if a shell is in a non-default runtime state. That's a lot more than not just calling exit -- you need to be robust against different IFS values; you need to be robust against functions or aliases overriding commands; you need to avoid modifying global variables the user's other interactive functions might depend on. Commented Aug 24, 2018 at 23:22
  • 2
    In general, it just doesn't make sense to attempt: Users have no reasonable expectation that they can source scripts that aren't explicitly written and documented for that mode of use. Commented Aug 24, 2018 at 23:23
  • 1
    ...and even worse/further, sourcing a script doesn't honor its shebang, so you need to be compatible with every interactive shell on the system the user might be using! So you need to be sure your script works not just with bash but with ksh/zsh/csh/etc... Commented Aug 24, 2018 at 23:25

1 Answer 1

23

The most common solution to bail out of a script without causing the parent shell to terminate is to try return first. If it fails then exit.

Your code will look like this:

#! /usr/bin/bash
# f.sh

func()
{
  return 42
}

func
retVal=$?
if [ "${retVal}" -ne 0 ]; then
  return ${retVal} 2>/dev/null # this will attempt to return
  exit "${retVal}" # this will get executed if the above failed.
fi

echo "don't wanna reach here"

You can also use return ${retVal} 2>/dev/null || exit "${retVal}".

Hope this helps.

Sign up to request clarification or add additional context in comments.

1 Comment

This is basically the answer (not the accepted one) to the question OP linked. Link to answer: stackoverflow.com/a/25811163 These two questions are probably duplicates.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.