Sourcing vs Executing a script

If you're a Unix-like user and are not familiar with the difference between executing a script and sourcing it, then this article is for you.

Prerequisites: Basic familiarity with: Bash, Shell sessions, environment variables and processes.

The story began a few days ago when I got bored with my terminal prompt, and I decided to customize it a bit. So I opened the .bashrc (equivalent of .zshrc for Zsh users) file and changed the PS1 variable to something more suitable for my mood at that specific moment (yeah...being moody is not easy). After saving the file and exiting, I noticed that the change was not applied yet, which led to an interesting exploration that I will share with you in this short article.

The content below applies to the ~/.bashrc file if you're using bash, or ~/.zshrc for those using Zsh, like Mac users. As for Windows people, feel free to continue playing Solitaire :]

.bashrc file

You can think of the .bashrc file as the one responsible for the configuration of your shell, so whenever you open a new terminal window, everything that is inside .bashrc will be applied to your shell session, whether it's environment variables, a customized prompt, or anything you include in that file. The thing to note here is that this file is read only in the beginning when a new shell process is spawned on your system. So if you are already working in your terminal window, and you make changes to these files, this will not be enough for the changes to be applied. You will either have to open a new terminal window, which will initiate a new shell session and read the .bashrc file, OR execute the .bashrc file using source:

source ~/.bashrc

Which will immediately re-run the .bashrc file and apply the changes you made to your current shell.

What is this so[u]rcery?

If you run source --help you will get the following:

Execute commands from a file in the current shell...

The key thing to note here is that executing a script with ./filename.sh will run the file in a subshell, whereas sourcing it will run it in the current shell session. For this to make more sense, let's go through the following example one step at a time:

Create a file called test.sh and add the following content:

#!/bin/bash 
echo $testvar

Or #!/bin/zsh if you're using Zsh.

Now exit Vim (yes, I assume you did not open vscode for two lines of code), make the file executable and add the following shell variable:

$ chmod +x ./test.sh
$ testvar=testing

This will add the testvar variable to your current shell session variables (which you can see by running set). What we need to pay attention to here is that this variable is ONLY available for the current shell session.

Now, let's run the following:

$ ./test.sh
$

As you can see, nothing was printed! Why? well, running a script like that will create a subshell with its own configurations and variables, and this subshell has no access to the variables defined in our initial shell, which is why it cannot find the testvar variable.

Now let's run the file again but using source this time:

$ source ./test.sh
testing
$

Voila! This demonstrates the description initially provided:

Execute commands from a file in the current shell.

So now the script does have access to the testvar variable because it is running in the same shell session.

To sum up, we saw how using source performs the following main tasks:

  1. Run a file line by line, executing the commands as they are read.
  2. Execute the file in the current shell session instead of in a new one, allowing access to the existing environment variables and configurations.

Note: On some systems you can use a dot . instead of source.

Example: . ~/.bashrc