Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

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}"
    else
        echo "$var is not set"
    fi
done

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 http://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.

share|improve this question
1  
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 at 16:44

7 Answers 7

up vote 6 down vote accepted

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
share|improve this answer
    
This works, but what is happening with/whithout quotes? Is my general approach correct the first place? –  Jasper Mar 25 at 16:11
    
Yeah, that's precognitive: I answered the question before it was asked... 8-) –  Hauke Laging Mar 25 at 16:13
2  
@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. –  Hauke Laging Mar 25 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 at 8:15
    
@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. –  Hauke Laging Mar 26 at 13:17

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}"
    else
        echo "$var is not set"
    fi
done

Works for me.

share|improve this answer

If you want the program stopped:

N= 
${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.

share|improve this answer

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

#!/bin/sh
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

share|improve this answer
    
And in your opinion this makes which sense for run time checks? –  Hauke Laging Mar 25 at 18:39
1  
@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 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 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. –  Hauke Laging Mar 26 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 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
share|improve this answer
    
I was asking about "multiple" variables, so I was looking for something like indirection... –  Jasper Mar 26 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 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:

one=1
three=3

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"
done
share|improve this answer

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.

one=#UNSET#
two=#UNSET#
three=#UNSET#

. set_vars_script

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

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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