bash source a file that has an exit command at the end

Viewed 643
cat >file1.sh <<'EOF_FILE1'
  echo 'before source'
  source 'file2.sh'
  echo 'after source'
  func1
EOF_FILE1

cat >file2.sh <<'EOF_FILE2'
  echo 'test script'
  func1() {
    echo 'func1 starts'
    exit
  }
  exit
EOF_FILE2

bash file1.sh

Intended output is:

before source
test script
after source
func1 starts

Actual output is:

before source
test script

The 'after source' is missing due to the exit command. Is there a way around this since I can not remove exit from the code?

2 Answers

Whereas the best approach is to write scripts that are intended to be sourced rather than executed with that use case in mind, if you can't do that for whatever reason, you might consider aliasing exit to return before the source command, as follows:

shopt -s expand_aliases  # enable alias expansion (off by default in noninteractive shells)
alias exit=return        # ...and alias 'exit' to 'return'

source 'file2.sh'        # source in your file which incorrectly uses 'exit' at top-level
unalias exit             # disable the alias...
echo 'after source'
func1

If you want the exit in your function to still take effect when that function is called, things can be made a little more complex:

maybe_exit() {
  local last_retval=$?                 # preserve exit's behavior of defaulting to $?
  [[ $do_not_really_exit ]] && return  # abort if flag is set
  (( $# )) && exit "$@"                # if arguments are given, pass them through
  exit "$last_retval"                  # otherwise, use the $? we captured above
}

shopt -s expand_aliases  # enable alias expansion (off by default in noninteractive shells)
alias exit=maybe_exit    # ...and alias 'exit' to 'maybe_exit'

do_not_really_exit=1     # set a flag telling maybe_exit not to really exit
source 'file2.sh'        # source in your file which incorrectly uses 'exit' at top-level
unset do_not_really_exit # clear that flag...
unalias exit             # disable the alias...
echo 'after source'
func1

source executes file2.sh inside the same shell that runs file1.sh.


You might want to use the bash command instead, so that a new shell is spawned for executing file2.sh:

echo 'before source'
bash file2.sh
echo 'after source'

OR

As suggested by @CharlesDuffy in the comments, you can use (source file2.sh) to source file2.sh in a subshell. This would isolate the code in the subprocess, but lets it access non-exported shell variables, like any other sourced' script can do. It also consumes less resources.

echo 'before source'
(source file2.sh)
echo 'after source'
Related