BASH Programming

Bash Source Example and How to Use It to Grow Scripts

If you are like me, the first time you watched *Fantastic Beast and Where to Find Them*, you asked yourself, “Where is Dumbledore?” The same thing happens when you start looking to use the source builtin in bash, asking, “What is this for?”

Here we introduce the bash source builtin, how to use it by example, and even have for some fantastic bash scripts.

What is the source?

Let’s find out. `man source` and see what you find out.

A source in bash is a builtin also found other shell scripting languages such as csh that allows you to read in source code outside of the shell. It can be used inside a script or interactively when typing a command into the terminal.

`man bash` followed by `/source filename` to find the section written about the source in the bash man page is also a good place to look if you are looking for the bash bible version of what is the source.

Enough about what source is in bash by definition; let’s discuss the source and its alias.

Bash source and its alias

Like most builtins in bash, it has an alias. That is the dot(.) notation

So, you will find that the following command lines are equivalent.

  1. source line using the keyword
    source file
  2. source line using dot(.) notation
    . file

Now that you have the capabilities to read in commands from the outside let’s dive into some harmless source examples.

Bash source example 1: reloading your .bashrc

When you update your .bashrc file, the changes may not make their way into the shell you are working in. To incorporate changes in the .bashrc file into the current shell without having to open a new shell, we have to reload it. That is where the source comes in.

source ~/.bashrc

That should do it. Now suppose we have a function that we would like to have available in the current shell located in some file. Let’s load the function into our shell.

Bash source example 2: loading functions into the shell

First, we need a function. Here one foo.

foo() { echo bar ; }
foo
# bar

As you can see, foo prints bar.

Now store foo into a file called test.

declare -f foo > test

The function foo is now stored in the file test.

Let’s modify the stored version of view and reload it into our shell. Once upon a time, I wrote a guide on how to use sed in bash scripts. If you are not sure what line 1 below does, I recommend reading it.

sed -i -e s/bar/___/ test

Now instead of printing bar, the stored version of foo prints ___. Here is the part where we source in the stored version of food and call it.

source ./test
foo
# ___

To verify what foo is defined as in your current shell, use the following command:

declare -f foo

For more information on using declare in bash see the guide.

Now that we have reloaded the .bashrc and loading functions into the shell added to our belt; let’s move on to the next step, keeping secrets.

Bash source example 3: keeping secrets

Often you must devise ways to keep secrets in any environment, especially if you are working with git. You don’t want to be storing credentials in your repository. That is for sure.

We opt out of storing our secrets in the repository by storing them somewhere else. To make this work, we need a source.

First, let’ create a secret file.

secret="the-secret"
declare -p secret > secret-file
unset secret

Now let’s see how the secret would work into a script.

main() {
test ! -f "secret-file" || source ${_}
echo secret: ${secret}
}
main

Note that secret only exists in the main function. You can verify this by trying to echo the variable secret after calling the main function.

main
echo ${secret:-no-secret}

The output line after main is either empty or a secret declared outside of the main function.

Now you see how we can keep secrets to some extent in bash using the source builtin.

We have shown that we do just about anything in bash when it comes to loading and offloading code in an out of files. Now let’s go into some serious black examples of source in bash scripts that I like.

Bash source example 4: beard.sh

Recently, I renamed one of my repos to beard.sh mainly because it was better than the last name (do you know what it was called?), and I usually can be spotted with a beard. Sorry ladies. But you are welcome to grow one too, if only when you write bash scripts. Anyways, there is a high chance that what we are going to find a good bash source example in my beard.

First, let’s run the quick start. Don’t worry. You don’t have to trust me.

{
(
clone() {
git clone https://github.com/temptemp3/${1}.git ${2:-${1}}
}
prompt-name() {
read -p "What's Me's name? (niceboy) " name
}
get-name() {
while [ ! "${name}" ]
do
prompt-name
done
}
exit-on-duplicate-name() {
test ! -d "${name}" || {
echo "Someone else has Me's name!"
exit
}
}
clone-sh2() {
test "${SH2}" || {
clone sh2
echo -e "\ndeclare -x SH2='$( realpath sh2 )'" >> ~/.bashrc
}
}
install() {
echo -e "\n${name}() { bash '$( realpath clipboy.sh )' \${@} ; }" >> ~/.bashrc
}
name=""
get-name
exit-on-duplicate-name
echo "Me's name is ${name}!"
clone beard.sh ${name}
cd ${name}
clone-sh2
install
)
}

I hope you named it something you are going to live with that isn’t embarrassing to the point you don’t want to go out in public.

I don’t know about you, but this time I think I am going to go with samwise.

What you may have noticed here is that we already have to reload our .bashrc file. Good things that we covered this early. I trust that I made a conscious decision to leave it out. Otherwise, expect the quickstart to be updated in the future.

samewise allows you to build scripts programmatically like ruby on rails or Laravel for bash scripting, with the premise that all the code that does something is stored in the scripts directory. So, what’s going on in the command’s directory. Let’s see.

clipboy-store-list() {
. $( dirname ${0} )/scripts/200305-store-list.sh ${@}
}

Source: 200305-clipboy-store-list.sh

As you see, we are sourcing scripts from the scripts directory. Why not allow scripts to do whatever they want in the commands directory? Because this is our magazine. No, not in my beard.

Just to be thorough, let’s see what code runs.

. ${SH2}/store.sh
_store-list() { { local key ; key="${1}" ; local value ; value="${2}" ; }
init-store-silent
for key in $( echo ${!store[@]} | xargs -i '-d ' echo {} )
do
echo ${key}: ${store[${key}]}
done
}
_store-list ${@}

Source: 200305-store-list.sh

The script contains an adequate amount of code with the main function. In other words, it’s going to change the world.

Note that in the first line, there is subtle sourcing of a file that contains functions used by the script. You are starting to see that a source command is a powerful tool that comes in handy when reusing code in bash.

Before moving on to bigger and better bash source examples, let’s give samwise a new command called quote.

samwise make:script quote
samwise make:subcommand quote

Now go into the script directory and modify the quote script as follows.

_quote() {
echo Potatoes!
}
_quote ${@}

Now run the new command we created for samwise called quote.

samwise quote

Yes, potatoes!

Now, what if you want to write a script inside a script and source it. You can. I have something like that laying around. I haven’t used it for some time, but I am sure there are some cowboys (or cowgirls) out there that might.

Bash source example 4: attr at its best

Way back, I decided that I was tired of the assignment operator and created that script call attr.sh that allows the creation of getter and setter functions for any variable name that just happens to use source.

Here is the gist of it:

Create a script with a function.

cat > temp < temp << EOF
${1}() {
echo echo from \${FUNCNAME}
}
EOF

}
new fun
. temp
$_

Although this is maybe just a toy program but is effectively what is don’t in attr.sh to implement dynamic functions in bash. Don’t forget to clean up after yourself.

The next script I will bring up is another script I wrote called build.sh that allows you to convert multiple scripts using source into a single script without dependencies. It doesn’t use the source builtin directly, but it takes advantage of the behavior of the source when run using bash -vp.

Bash source example 4: build them all

Here are lines do most of the work in build.sh.

{ # resolve source lines
bash -vp ${0} true 2>&1 |
grep -v -e '^\s*[.]\s\+'
} | tee ${outfile}.sh

Let’s break it down.

bash -vp ${0} true 2>&1
bash -vp ${0}

runs itself and prints every line read including scripts that are sourced.

2>&1

redirects secondary output (or standard error) to standard output so that we can pipe it to another command.

grep -v -e '^\s*[.]\s\+'

exclude source lines that would otherwise cause a segmentation fault.

In the end, we can convert complex scripts requiring any number of dependencies into a single standalone script for people that we don’t want to install anything.

Enough about my scripts, let’s see if I can find anything interesting to note on GitHub. Sorry to say I tried. If you find anything, please feel free to let me know.

Source takes arguments!

One thing that is easy to miss is the fact that the source takes arguments!

The reason that you would supply arguments to a script in the source is to control runtime behavior. Suppose the script you want to source has modes that can be selected by providing an argument. We may select one of its modes by providing the mode we want to select as an argument to the source following the filename as follows.

source filename mode

Source behavior can be modified!

By default, the source may load any file existing in your path. However, if you want the source to run without having the path available, you could use the command as follows.

shopt -u sourcepath

Exercises

  1. Update your .bashrc file and reload it using source. Don’t forget to verify the change somehow.
  2. Find a script with functions. Move the functions to another file and source them. Verify that the script works as before.
  3. Find a script with hard-coded values. Move the hardcoded values into another file as a variable. Use the source to include them in the original script. Verify the script still works.
  4. Run the script in exercises 1 through 3 using bash -vp

TLDR;

I hope that you are now familiar with how the source works in bash. My advice on the source is to stick to the basics like loading config files and functions and maybe think about using it for something else later that may not be obvious in the beginning while you are still not comfortable with bash programming. Don’t worry. You’ll get there eventually.

About the author

Nicholas Shellabarger

Nicholas Shellabarger

A developer and advocate of shell scripting and vim. His works include automation tools, static site generators, and web crawlers written in bash. For work he tools with cloud computing, app development, and chatbots. He codes in bash, python, or php, but is open to offers.