In my previous blog post I explained a trick for setting bash env variables when you run a script. In this post I discuss how to write bash scripts that handle such variables properly.
Skip to the end if you just want the answer.
An environment variable, in bash, is for describing the environment.
For example, your home directory (
$HOME), your search path
$PATH), your preferred text editor (
Some variables are set for the user (
$PATH) and others are
optional. For example
$EDITOR simply overrides the default.
I often write a suite of scripts that require certain variables: the name of the database server to access, the URL of the Git repo being used, and so on.
How do we code this in bash? Usually we end up with a ton of if/then statements:
That’s so ugly! It’s not just ugly but it also violates the DRY principle.
Certainly there’s an easier way to do this? There sure is!
- Access the variable:
- Access the variable, with a default:
Here’s how it looks in a script:
:- means if the variable is unset or empty, uses the default
However including the default value every time you access the variable is a pain in the butt.
Can we do better? Sure!
There is also
:= which is the same thing as
but it also updates the variable with the default value
at the same time.
- Access the variable, set if empty:
Wait… what? A way to use a variable and have it be mutated along the way? Yes, that’s right. (A Haskell programmer somewhere just burst out in tears.)
Let’s write short script and watch the interactions.
Here’s the output:
Using this is real code
If we use
:- we have to use it every time we use
the variable, and if we have to change the default value we have to
change it everywhere it appears in the code.
If we use
:= we only have to specify the default value
the first time we use the variable. Sadly that means if we add code
earlier in the script, we have to remember to move the default to this
earlier code. That’s just asking for trouble.
So what do we do?
We need an excuse to use the variable (with the
early in the program so that all other uses don’t need
We could do this (BUT YOU SHOULDN’T):
That would work but it is ugly.
Luckily bash has a “do nothing” command called
What? Well, the
: command (yes, that’s a bash command) means
“ignore the rest of this command”. However, that is processed after
the variable substitution, so we get our variable modified (if unset
or empty) and that’s it.
Clear as mud, right?
I try to put all of these at the start of the file. This centralizes all the settings that users can override, makes sure that the overrides are done at the beginning, and is probably better for a few reasons I’m forgetting right now.
Here’s what that looks like, taken from a recent script I wrote (names changed, of course):
Most of the names are prefixed with with
FOO_ because because this
is for Project Foo. This prevents name collisions between projects.
FOO_THING’s default value comes from another variable! This is
usually done when the default is generated from other data. For
example, I recently wrote a script that had a special default when the
script was run non-interactively.
Now you have an easy way to use env variables but allow users to override them easily.
Set a variable. Override any previous value:
If you want a user to be able to override a variable:
Display the value but substitute a default if it is empty:
This is all covered in the bash(1) man page (The command
will view this) in a section called
Parameter Expansion. You
probably skipped that section because without the above context, I
doubt anyone understands why these features are in bash.
Now you know.