Icon of a thin page Icon of a thick page

Source Code

David A. Harding

I try to write shell scripts that do one thing and do it tolerably. Often that means I write more than one script for any non-trivial project. For example, I have about half-a-dozen scripts that help me maintain my website. I like sharing variables—mostly file and path names—between related scripts so I only need to define a variable once and only need to redefine it once if something changes. In bash or any POSIX shell, this is easy: add the variable definitions to a file and use the shell's builtin source function. For example, I put the following lines in the file my.vars in my home directory:

HI="Hello,"
YOU=`whoami`

The following script sources the variables and uses them:

#!/bin/bash

source $HOME/my.vars

echo "$HI $YOU!"

The output depends on my username. On my desktop, my username is harda.

Hello, harda!


The POSIX Shell
The POSIX specification, a set of rules for how Unix like operating systems should work named by Richard Stallman, doesn't define source as a shell builtin even though shells like bash and zsh support it. For interoperability, replace source with a period.

. $HOME/my.vars

By default, if I don't provide a complete path to the file to be sourced, the shell will look for the file in the directories listed in the $PATH environmental variable. bash will also search the current working directory—this lets me use a relative path. I always use complete path so I can run my scripts from any directory and without worrying about $PATH.


Checking for Errors
If I expect a variable named $TMPDIR and run the command, rm -rf $TMPDIR, but the variable hasn't been defined, I'll delete everything in the current directory. This can happen if a file doesn't get sourced.

The simplest way to prevent something like this from happening is to change the beginning of my script from #!/bin/bash to #!/bin/bash -u Now if I use a variable that hasn't been defined, bash will exit with an error before running a command that uses an undefined variable. For example, if I add -u to the beginning of the program above, delete the file my.vars, and re-run the program, the output changes:

./foo.sh: line 3: /home/harda/my.vars: No such file or directory
./foo.sh: line 5: HI: unbound variable

The good news is bash told me there was a problem. The bad news is there are two error messages but only one cause: the file I sourced is missing. A little more work will make things clearer. Leaving the -u switch in, I can check for errors in the source statement:

. $HOME/my.vars || {  
        echo "Can't source my.vars!"
        echo "I'm exiting with an error code now.  Sorry."
        exit 1
 }

The above code says, source my.vars (. $HOME/my.vars) and if it doesn't work (||) run all of the code between the curly braces ({}). exit means stop running the program; its single optional argument sets the return code. 0, the value if I don't specify a code, means the program exited true (i.e. successfully); any number between 1 and 255 means an error or failure occurred. Using the code above, and with my.vars still deleted, the error output of the program becomes clearer:

        ./foo.sh: line 3: /home/harda/my.vars: No such file or directory
        Can't source my.vars!
        I'm exiting with an error code now.  Sorry.


Final Code

#!/bin/bash -u

. $HOME/my.vars || {
        echo "Can't source my.vars!"
        echo "I'm exiting with an error code now.  Sorry."
        exit 1
        }

echo "$HI $YOU!"