Why do I suddenly need to use source to call ANY Bash script?

This week, I ran into a somewhat weird and annoying Bash issue that took a couple of minutes to solve. It was a very simple problem, but it caused quite a bit of headaches. A colleague wrote a script that was setting up some configuration variables. The script, named config.sh, was intended to be called using source config.sh. The source command tells Bash to run the script in the current interpreter rather than spawning a new process and run in it. Any variable in such a sourced script gets set up in the current Bash process, so they can be used after the script finished.

The script was containing variables of the form

TOOLS_DIR=<some path>
TSVTOOLS_DIR=<some path>

I tried to refer to these variables in one of my script, by using $TOOLS_DIR, for example. However, each time I was calling the script, Bash was acting the same was as if the variable wasn’t defined! The variable was accessible from the Bash process that invoked the config.sh script. Why?

There were two workarounds:

  1. Call my script using source.
  2. Modify my script to call source config.sh in it. I didn’t like this, because this was adding an extra step to all my scripts and running config.sh was taking several seconds.

My colleague and I looked at this to no avail. Then I found the culprit.

The config.sh script was declaring variables local to the Bash process. For the variables to be transferred to forked processes like invoked scripts, they needed to be exported as environment variabies! So the solution was as simple as modifying config.sh to prefix every variable declaration with export! For example,

export TOOLS_DIR=<some path>
export TSVTOOLS_DIR=<some path>

After this very simple change, I was able to use the variables in my script, without sourcing the scripts or invoking config.sh locally.