Categories
bash shell variables

How to check if a variable is set in Bash

2154

How do I know if a variable is set in Bash?

For example, how do I check if the user gave the first parameter to a function?

function a {
    # if $1 is set ?
}

12

  • 15

    if test $# -gt 0; then printf 'arg <%s>\n' "[email protected]"; fi.

    – Jens

    Jul 9, 2013 at 16:57

  • 263

    Note to solution-seekers: There are many highly-rated answers to this question that answer the question “is variable non-empty”. The more correction solutions (“is variable set”) are mentioned in answers by Jens and Lionel below.

    Nov 29, 2013 at 17:56

  • 10

    Also Russell Harmon and Seamus are correct with their -v test, although this is seemingly only available on new versions of bash and not portable across shells.

    – Graeme

    Jan 28, 2014 at 17:58


  • 5

    As pointed out by @NathanKidd, correct solutions are given by Lionel and Jens. prosseek, you should switch your accepted answer to one of these.

    – Garrett

    Feb 13, 2014 at 23:22


  • 3

    … or the incorrect answer could be downvoted by the more discerning among us, since @prosseek is not addressing the problem.

    – dan3

    Jul 15, 2014 at 9:46

3043

(Usually) The right way

if [ -z ${var+x} ]; then echo "var is unset"; else echo "var is set to '$var'"; fi

where ${var+x} is a parameter expansion which evaluates to nothing if var is unset, and substitutes the string x otherwise.

Quotes Digression

Quotes can be omitted (so we can say ${var+x} instead of "${var+x}") because this syntax & usage guarantees this will only expand to something that does not require quotes (since it either expands to x (which contains no word breaks so it needs no quotes), or to nothing (which results in [ -z ], which conveniently evaluates to the same value (true) that [ -z "" ] does as well)).

However, while quotes can be safely omitted, and it was not immediately obvious to all (it wasn’t even apparent to the first author of this quotes explanation who is also a major Bash coder), it would sometimes be better to write the solution with quotes as [ -z "${var+x}" ], at the very small possible cost of an O(1) speed penalty. The first author also added this as a comment next to the code using this solution giving the URL to this answer, which now also includes the explanation for why the quotes can be safely omitted.

(Often) The wrong way

if [ -z "$var" ]; then echo "var is blank"; else echo "var is set to '$var'"; fi

This is often wrong because it doesn’t distinguish between a variable that is unset and a variable that is set to the empty string. That is to say, if var="", then the above solution will output “var is blank”.

The distinction between unset and “set to the empty string” is essential in situations where the user has to specify an extension, or additional list of properties, and that not specifying them defaults to a non-empty value, whereas specifying the empty string should make the script use an empty extension or list of additional properties.

The distinction may not be essential in every scenario though. In those cases [ -z "$var" ] will be just fine.

45

  • 47

    @Garrett, your edit has made this answer incorrect, ${var+x} is the correct substitution to use. Using [ -z ${var:+x} ] produces no different result than [ -z "$var" ].

    – Graeme

    Feb 13, 2014 at 22:08

  • 15

    This doesn’t work. I’m getting “not set” regardless of whether var is set to a value or not (cleared with “unset var”, “echo $var” produces no output).

    – Brent212

    Sep 9, 2014 at 22:04

  • 14

    For the solution’s syntax ${parameter+word}, the official manual section is gnu.org/software/bash/manual/… ; however, a bug in that, it doesn’t very clearly mention this syntax but says just quote(Omitting the colon[ “:”] results in a test only for a parameter that is unset. .. ${parameter:+word} [means] If parameter is null or unset, nothing is substituted, otherwise the expansion of word is substituted.); the cited ref pubs.opengroup.org/onlinepubs/9699919799/utilities/… (good to know)has much clearer docs here.

    Nov 8, 2014 at 15:04

  • 8

    It looks like quotes are required when you use multiple expressions, e.g. [ -z "" -a -z ${var+x} ] gets bash: [: argument expected in bash 4.3-ubuntu (but not e.g. zsh). I really dislike the answer using a syntax that happens to work in some cases.

    – FauxFaux

    Jun 11, 2015 at 11:32

  • 65

    Using a simple [ -z $var ] is no more “wrong” than omitting quotes. Either way, you’re making assumptions about your input. If you are fine treating an empty string as unset, [ -z $var ] is all you need.

    – nshew13

    May 5, 2016 at 20:09

1201

To check for non-null/non-zero string variable, i.e. if set, use

if [ -n "$1" ]

It’s the opposite of -z. I find myself using -n more than -z.

You would use it like:

if [ -n "$1" ]; then
  echo "You supplied the first parameter!"
else
  echo "First parameter not supplied."
fi

12

  • 105

    I usually prefer [[ ]] over [ ], as [[ ]] is more powerfull and causes less problems in certain situations (see this question for an explanation of the difference between the two). The question specifically asks for a bash solution and doesn’t mention any portability requirements.

    – Flow

    Oct 13, 2013 at 15:26

  • 24

    The question is how one can see if a variable is set. Then you can’t assume that the variable is set.

    Nov 12, 2013 at 13:15

  • 9

    I agree, [] works better especially for shell (non-bash) scripts.

    – Hengjie

    Jun 2, 2014 at 13:09

  • 81

    Note that -n is the default test, so more simply [ "$1" ] or [[ $1 ]] work as well. Also note that with [[ ]] quoting is unnecessary.

    – tne

    Nov 26, 2014 at 13:49

  • 5

    Seems it’s not really opposite… Real opposite is if [ ! -z "$1" ]

    Aug 16, 2018 at 20:34

699

Here’s how to test whether a parameter is unset, or empty (“Null”) or set with a value:

+--------------------+----------------------+-----------------+-----------------+
|   Expression       |       parameter      |     parameter   |    parameter    |
|   in script:       |   Set and Not Null   |   Set But Null  |      Unset      |
+--------------------+----------------------+-----------------+-----------------+
| ${parameter:-word} | substitute parameter | substitute word | substitute word |
| ${parameter-word}  | substitute parameter | substitute null | substitute word |
| ${parameter:=word} | substitute parameter | assign word     | assign word     |
| ${parameter=word}  | substitute parameter | substitute null | assign word     |
| ${parameter:?word} | substitute parameter | error, exit     | error, exit     |
| ${parameter?word}  | substitute parameter | substitute null | error, exit     |
| ${parameter:+word} | substitute word      | substitute null | substitute null |
| ${parameter+word}  | substitute word      | substitute word | substitute null |
+--------------------+----------------------+-----------------+-----------------+

Source: POSIX: Parameter Expansion:

In all cases shown with “substitute”, the expression is replaced with the value shown. In all cases shown with “assign”, parameter is assigned that value, which also replaces the expression.

To show this in action:

+--------------------+----------------------+-----------------+-----------------+
|   Expression       |  When FOO="world"    |  When FOO=""    |    unset FOO    |
|   in script:       |  (Set and Not Null)  |  (Set But Null) |     (Unset)     |
+--------------------+----------------------+-----------------+-----------------+
| ${FOO:-hello}      | world                | hello           | hello           |
| ${FOO-hello}       | world                | ""              | hello           |
| ${FOO:=hello}      | world                | FOO=hello       | FOO=hello       |
| ${FOO=hello}       | world                | ""              | FOO=hello       |
| ${FOO:?hello}      | world                | error, exit     | error, exit     |
| ${FOO?hello}       | world                | ""              | error, exit     |
| ${FOO:+hello}      | hello                | ""              | ""              |
| ${FOO+hello}       | hello                | hello           | ""              |
+--------------------+----------------------+-----------------+-----------------+

13

  • 7

    @HelloGoodbye Yes it does work: set foo; echo ${1:-set but null or unset} echos “foo”; set --; echo ${1:-set but null or unset} echoes set but null …

    – Jens

    Nov 27, 2013 at 13:07

  • 6

    @HelloGoodbye The positional parameters can be set with, uh, set 🙂

    – Jens

    Nov 28, 2013 at 7:59

  • 137

    This answer is very confusing. Any practical examples on how to use this table?

    – Ben Davis

    Sep 27, 2014 at 17:16

  • 14

    @BenDavis The details are explained in the link to the POSIX standard, which references the chapter the table is taken from. One construct I use in almost any script I write is : ${FOOBAR:=/etc/foobar.conf} to set a default value for a variable if it is unset or null.

    – Jens

    Mar 10, 2015 at 11:08

  • 3

    parameter is any variable name. word is some string to substitute depending on which syntax in the table you are using, and whether the variable $parameter is set, set but null, or unset. Not to make things more complicated, but word can also include variables! This can be very useful for passing optional args separated by a character. For example: ${parameter:+,${parameter}} outputs a comma separated ,$parameter if it is set but not null. In this case, word is ,${parameter}

    Jan 19, 2016 at 23:30