TL;DR

Finding out if an environment variable is defined, in the shell.

Definitions

First of all, here’s what I mean:

  • undefined: when the variable is not set;
  • defined: when the variable is set, which has two sub-cases:
    • empty: when the variable is set to an empty string (this is also referred to as null);
    • other: anything that has length greater than 0.

Note that an empty (or null) variable is defined.

We will assume the following in the examples below:

unset UNDEF
EMPTY=''
LENGTHY='whatever'

Basic trick

We start from the Use Alternate Value in parameter expansion:

`${parameter:+word}

Use Alternate Value. If parameter is null or unset, nothing is substituted, otherwise the expansion of word is substituted.

Test time:

$ printf "UNDEF<${UNDEF:+X}> EMPTY<${EMPTY:+X}> LENGTHY<${LENGTHY:+X}>\n"
UNDEF<> EMPTY<> LENGTHY<X>

Almost there, we can tell non-empty strings apart, but undefined strings are treated the same as empty ones. Anyway, parameter expansion also has this:

In the parameter expansions shown previously, use of the in the format shall result in a test for a parameter that is unset or null; omission of the shall result in a test for a parameter that is only unset.

So, it seems that we only have to get rid of the colon:

$ printf "UNDEF<${UNDEF+X}> EMPTY<${EMPTY+X}> LENGTHY<${LENGTHY+X}>\n"
UNDEF<> EMPTY<X> LENGTHY<X>

This is what we were looking for: undefined variables are turned into an empty string, defined variables are turned into the provided string X.

Adding readability

The basic trick is not the most readable thing in the world… nor the easier to remember. For me at least. This is why I prefer to embed the trick in a function:

is_var_defined () { eval "[ -n \"\${$1+ok}\" ]" ; }

The assumption in the above function is that the variable name is passed as the first parameter - this implies using an eval to make things work.

Test time:

$ is_var_defined UNDEF && printf 'defined\n' || printf 'undefined\n'
undefined

$ is_var_defined EMPTY && printf 'defined\n' || printf 'undefined\n'
defined

$ is_var_defined LENGTHY && printf 'defined\n' || printf 'undefined\n'
defined

This is it for today!