1

Edited for clarification

I have a script which should normally be executed. However, for testing purposes I'd like to be able to source it to get the definitions of all functions and variables, and also to set up the environment as needed and create directories, and then return before the meaty part begins. Basically I need to get an interactive shell with the environment as it would look after some checks are performed, but only if all checks pass.

The code flow looks something like this:

#!/bin/bash

SOME_VAR="hey"
checker() {
  # can find fatal conditions which should exit the script
  # or stop sourcing it
  # ... do some checks
  # if all OK
  return 0

  # otherwise stop
  if [ "${0}" != "${BASH_SOURCE[0]}" ] ; then
    : #!!!! we're being sourced, what to do here??
  fi
  exit 1
}

# some more function definitions...

# do some checks to see if the current environment is supported
checker "some_fatal_condition" # need to stop sourcing here
checker "some_other_condition"

# if the script is being sourced for testing purposes, 
# and all ok up to here, stop now
return 2>/dev/null

# actual stuff follows, only if executing
# ...

My question is, when sourcing it, how can I stop processing the sourced script without exiting the shell function from inside the function checker?

I should add that doing something like

checker "some_fatal_condition" || return 2>/dev/null

is not an option in this case (there are many more functions which may need to exit and may be called from other functions, etc).

I have a couple of things in mind on how to achieve what I want without sourcing it from my main shell session.

  • Suggestion 1: Start a nested shell from within my shell and source the script there. This way it's not a big problem if the script exits while sourced.

  • Suggestion 2: Do not allow sourcing the script, rather pass a command-line option that tells it "this is test only" and start an interactive shell at the point where I need it. Afterwards exit the script before the meaty part.

The problem with Suggestion 2 is that I don't know how to start an interactive shell from within a script that inherits all defined variables, functions, shell options, etc... How can I do that?

  • 1
    I'm interested in the real answer to this question, but I advise against using source as a test invocation mechanism. Sharing the state (environment, fds, etc) and process of the executing shell - what sourcing is - only makes your test environment bizarre and unlike your normal execution environment. You can see that if you were executing the code, it would be easy to describe the intended flow with your existing flow control mechanisms. It's only that you need to deal specially with exit that you are stuck on this problem, right? – Daniel Farrell Oct 24 at 19:39
  • 1
    Running the script as a "dot-script" (with source) for testing is not advised. You'll change the current shell environment by sourcing the script, which in turn may impact on the next test run of the script. – Kusalananda Oct 24 at 19:49
  • @DanielFarrell I added a clarification on what I actually want to achieve. Maybe sourcing the script is not the only/best way – Aayla Secura Oct 24 at 20:44
0

You should not exit from within a function unless it's explicitly an error/exit handler. It's preferable to return from a function.

Another way to tease apart the functions from the script body is to do exactly that: put your functions into a separate file, and source that into the script.

  • The checks are fatal and meant to exit indeed. The function definitions depend on the environment so the checks are needed even if testing it only. But if check A fails, the rest should not proceed. I edited my question to clarify. – Aayla Secura Oct 24 at 20:46
  • separate your error handler into a different function – Rich Oct 24 at 21:32
0

Ok, I found how to automatically export variables, so that I can start an interactive shell for testing: set -a at the top of the script.

#!/bin/bash

if [ "${0}" != "${BASH_SOURCE[0]}" ] ; then
  echo "This script should not be sourced" >&2
  return 1
fi

set -a # automatically export all vars
SOME_VAR="hey"
checker() {
  # can find fatal conditions which should exit the script
  # or stop sourcing it
  # ... do some checks
  # if all OK
  return 0

  # otherwise stop
  exit 1
}

# some more function definitions...

# do some checks to see if the current environment is supported
checker "some_fatal_condition" # need to stop sourcing here
checker "some_other_condition"

# if the script is being sourced for testing purposes, stop now
if [ "${1}" == "test" ] ; then
  PS1="test> " bash
  exit $?
fi

echo "proceeding"
# actual stuff follows, only if executing
# ...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.