I'd like to make sure that at a certain point of a script, after sourceing a configuration file, several variables are set and, if they are not, to stop execution, telling the user about the missing variable. I have tried

for var in $one $two $three ; do

but if for example $two is not set, the loop is never executed for $two. The next thing I tried was

for var in one two three ; do
    if [ -n ${!var} ] ; then
        echo "$var is set to ${!var}"
        echo "$var is not set"

But if two is not set, I still get "two is set to" instead of "two is not set".

How can I make sure that all required variables are set?

Update/Solution: I know that there is a difference between "set" and "set, but empty". I now use (thanks to https://stackoverflow.com/a/16753536/3456281 and the answers to this question) the following:

if [ -n "${!var:-}" ] ; then

so, if var is set but empty, it is still considered invalid.

  • 3
    You can also add set -u to the beginning of your script to terminate it immediately when an unset variable is used.
    – n.st
    Mar 25, 2014 at 16:44

9 Answers 9


Quoting error.

if [ -n "${!var}" ] ; then

For the future: Setting

set -x

before running the code would have shown you the problem. Instead of adding that to the code you can call your script with

bash -vx ./my/script.sh
  • This works, but what is happening with/whithout quotes? Is my general approach correct the first place?
    – Jasper
    Mar 25, 2014 at 16:11
  • Yeah, that's precognitive: I answered the question before it was asked... 8-) Mar 25, 2014 at 16:13
  • 4
    @Jasper You should always quote variables. That does not cost more time than thinking about whether this is necessary every time. But it avoids the errors. Mar 25, 2014 at 16:18
  • On the contrary, quoting only protects against expansion. If the expansion is what youre interested in, quoting is certainly not the way to go. For instance: cs=673,290,765, ; set -- $(IFS=, ; echo $cs) ; echo $# ; output : 3
    – mikeserv
    Mar 26, 2014 at 8:15
  • 2
    @mikeserv Only single quotes protect against expansion what I obviously do not suggest. Double quotes protect against word splitting. I have not denied that there may be cases where quoting must be omitted. But that is not an argument against my general rule. What kind of people is going to use something like set -- $(IFS=, ; echo $cs)? The kind of people who have to ask here why if [ -n ${!var} ] ; then doesn't work? Probably not. Mar 26, 2014 at 13:17

If you want the program stopped:

${one?var 1 is unset} 
${two:?var 2 is unset or null}
${three:+${N:?var 3 is set and not null}}

That'll do the trick. Each of the messages following the question mark is printed to stderr and the parent shell dies. Well, OK, so not each message - only one - just the first one that fails prints a message cause the shell dies. I like to use these tests like this:

( for v in "$one" "$two" "$three" ; do
    i=$((i+1)) ; : ${v:?var $i is unset or null...} 
done ) || _handle_it

I had a lot more to say about this here.


Only thing you need are quotes in your test:

for var in one two three ; do
    if [ -n "${!var}" ] ; then
        echo "$var is set to ${!var}"
        echo "$var is not set"

Works for me.


A solution that is maximally friendly tests all the requirements, and reports them together, rather than failing at the first one and requiring back-and-forth to get things right:


required_vars=(one two three)

for i in "${required_vars[@]}"
    test -n "${!i:+y}" || missing_vars+=("$i")
if [ ${#missing_vars[@]} -ne 0 ]
    echo "The following variables are not set, but should be:" >&2
    printf ' %q\n' "${missing_vars[@]}" >&2
    exit 1

I use an array variable to track which variables haven't been set, and use the result to compose a user-facing message.


  • I quoted ${required_vars[@]} in the for loop mainly out of habit - I wouldn't advise including any shell metacharacters in your variable names!
  • I didn't quote ${#missing_vars[@]}, because that's always an integer, even if you are perverse enough to disregard the preceding advice.
  • I used %q when printing; %s would normally be sufficient.
  • Error output always goes to the error stream with >&2, so it doesn't get piped to downstream commands
  • Silence is golden - don't print progress information or debugging info unless specifically asked. That makes errors more obvious.

You can add

set -u

to the beginning of your script to make it terminate when it tries to use an unset variable.

A script like

set -u
echo $foo

will result in

script.sh: 3: script.sh: foo: parameter not set

If you're using bash instead, the error will look like this:

script.sh: line 3: foo: unbound variable

  • And in your opinion this makes which sense for run time checks? Mar 25, 2014 at 18:39
  • 2
    @HaukeLaging I don't quite follow you — set -u prevents exactly the kind of error the OP is trying to avoid and (in contrary to all other solutions) isn't limited to a specific set of variables. It is in fact a useful precaution for almost all shell scripts to have them fail safely instead of doing unexpected things when a variable is unset. This article is a handy reference on the topic.
    – n.st
    Mar 25, 2014 at 18:55
  • @n.st I disagree - null can be just as useful a value as not null if you plan for it.
    – mikeserv
    Mar 26, 2014 at 8:02
  • 1
    @n.st This doesn't make any sense for the OP as he does not want a protection against accessing unset variables (BTW: a set but empty variable would cause the same error but nor react to your "protection"). He wants a run time check whether a variable is unset / empty. Your suggestion may be useful as a general development help but does not solve the OP's problem. Mar 26, 2014 at 13:23
  • set -u could be used as well I think, but it is not as flexible as proper parameter expansion (see my updated question). And with set -u I'd have to make a dummy-access to all target variables if I want to check their presence in one place.
    – Jasper
    Mar 27, 2014 at 11:58

bash 4.2 lets you test if a variable is set with the -v operator; an unset variable and a variable set to the empty string are two different conditions:

$ unset foo
$ [[ -v foo ]] && echo foo is set
$ [[ -z "$foo" ]] && echo foo is empty
foo is empty
$ foo=
$ [[ -v foo ]] && echo foo is set
foo is set
$ [[ -z "$foo" ]] && echo foo is empty
foo is empty
  • I was asking about "multiple" variables, so I was looking for something like indirection...
    – Jasper
    Mar 26, 2014 at 13:26
  • You don't need indirection, since -v takes the name of a variable, so for var in one two three; [[ -v $var ]] && ...; done would check if each of one, two, and three are set in sequence.
    – chepner
    Mar 26, 2014 at 13:29

I think if you mean not set, so the variable must never be initialized. If you use [ -n "${!var}" ], so the empty variable like two="" will be failed, while it is set. You can try this:


for var in one two three; do
  declare -p $var > /dev/null 2>&1 \
  && printf '%s is set to %s\n' "$var" "${!var}" \
  || printf '%s is not set\n' "$var"

A concise (though slightly hacky) solution, to handle the case where it's OK for the sourced script to set the variables to a null value, is to initialise the variables to some special value before sourcing the script, and then check for that value afterwards, e.g.


. set_vars_script

if [[ $one$two$three == *#UNSET#* ]] ; then
  echo "One or more essential variables are unset" >&2
  exit 1
  • 1
    another nice solution, but I'd like to give a more specific error message then "one or more variables unset"...
    – Jasper
    Mar 28, 2014 at 9:45

I've extended the @mikeserv's answer.

This version takes a list of variables to check the presence of, printing the name of the missing variable and triggering a call to usage() on error.

( i=0; for var_name in ${REQD_VALUES[@]}; do
    VALUE=${!var_name} ;
    i=$((i+1)) ; : ${VALUE:?$var_name is missing}
done ) || usage

Example output when a value is missing:

./myscript.sh: line 42: VALUE: OTHER_VARIABLE is missing

Note that you can change the variable called VALUE to an alternative name that suits your desired output better.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.