Home » How to » shell – How to check if a variable is set in Bash?

shell – How to check if a variable is set in Bash?

Posted by: admin February 21, 2020 Leave a comment


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 ?
How to&Answers:

(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.


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!"
  echo "First parameter not supplied."


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

|                    |       parameter      |     parameter   |    parameter    |
|                    |   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.


While most of the techniques stated here are correct, bash 4.2 supports an actual test for the presence of a variable (man bash), rather than testing the value of the variable.

[[ -v foo ]]; echo $?
# 1

[[ -v foo ]]; echo $?
# 0

[[ -v foo ]]; echo $?
# 0

Notably, this approach will not cause an error when used to check for an unset variable in set -u / set -o nounset mode, unlike many other approaches, such as using [ -z.


There are many ways to do this with the following being one of them:

if [ -z "$1" ]

This succeeds if $1 is null or unset


I always find the POSIX table in the other answer slow to grok, so here’s my take on it:

   |   if VARIABLE is:    |    set     |         empty         |        unset          |
 - |  ${VARIABLE-default} | $VARIABLE  |          ""           |       "default"       |
 = |  ${VARIABLE=default} | $VARIABLE  |          ""           | $(VARIABLE="default") |
 ? |  ${VARIABLE?default} | $VARIABLE  |          ""           |       exit 127        |
 + |  ${VARIABLE+default} | "default"  |       "default"       |          ""           |
:- | ${VARIABLE:-default} | $VARIABLE  |       "default"       |       "default"       |
:= | ${VARIABLE:=default} | $VARIABLE  | $(VARIABLE="default") | $(VARIABLE="default") |
:? | ${VARIABLE:?default} | $VARIABLE  |       exit 127        |       exit 127        |
:+ | ${VARIABLE:+default} | "default"  |          ""           |          ""           |

Note that each group (with and without preceding colon) has the same set and unset cases, so the only thing that differs is how the empty cases are handled.

With the preceding colon, the empty and unset cases are identical, so I would use those where possible (i.e. use :=, not just =, because the empty case is inconsistent).


  • set means VARIABLE is non-empty (VARIABLE="something")
  • empty means VARIABLE is empty/null (VARIABLE="")
  • unset means VARIABLE does not exist (unset VARIABLE)


  • $VARIABLE means the result is the original value of the variable.
  • "default" means the result was the replacement string provided.
  • "" means the result is null (an empty string).
  • exit 127 means the script stops executing with exit code 127.
  • $(VARIABLE="default") means the result is the original value of the variable and the replacement string provided is assigned to the variable for future use.


To see if a variable is nonempty, I use

if [[ $var ]]; then ...       # `$var' expands to a nonempty string

The opposite tests if a variable is either unset or empty:

if [[ ! $var ]]; then ...     # `$var' expands to the empty string (set or not)

To see if a variable is set (empty or nonempty), I use

if [[ ${var+x} ]]; then ...   # `var' exists (empty or nonempty)
if [[ ${1+x} ]]; then ...     # Parameter 1 exists (empty or nonempty)

The opposite tests if a variable is unset:

if [[ ! ${var+x} ]]; then ... # `var' is not set at all
if [[ ! ${1+x} ]]; then ...   # We were called with no arguments


On a modern version of Bash (4.2 or later I think; I don’t know for sure), I would try this:

if [ ! -v SOMEVARIABLE ] #note the lack of a $ sigil
    echo "Variable is unset"
elif [ -z "$SOMEVARIABLE" ]
    echo "Variable is set to an empty string"
    echo "Variable is set to some string"


if [ "$1" != "" ]; then
  echo \ is set
  echo \ is not set

Although for arguments it is normally best to test $#, which is the number of arguments, in my opinion.

if [ $# -gt 0 ]; then
  echo \ is set
  echo \ is not set


For those that are looking to check for unset or empty when in a script with set -u:

if [ -z "${var-}" ]; then
   echo "Must provide var environment variable. Exiting...."
   exit 1

The regular [ -z "$var" ] check will fail with var; unbound variable if set -u but [ -z "${var-}" ] expands to empty string if var is unset without failing.


You want to exit if it’s unset

This worked for me. I wanted my script to exit with an error message if a parameter wasn’t set.

#!/usr/bin/env bash

set -o errexit

# Get the value and empty validation check all in one
VER="${1:?You must pass a version of the format 0.0.0 as the only argument}"

This returns with an error when it’s run

[email protected]:~$ ./setver.sh
./setver.sh: line 13: 1: You must pass a version of the format 0.0.0 as the only argument

Check only, no exit – Empty and Unset are INVALID

Try this option if you just want to check if the value set=VALID or unset/empty=INVALID.

TSET="good val"
unset TUNSET

if [ "${TSET:-}" ]; then echo "VALID"; else echo "INVALID";fi
if [ "${TEMPTY:-}" ]; then echo "VALID"; else echo "INVALID";fi
if [ "${TUNSET:-}" ]; then echo "VALID"; else echo "INVALID";fi

Or, Even short tests 😉

[ "${TSET:-}"   ] && echo "VALID" || echo "INVALID"
[ "${TEMPTY:-}" ] && echo "VALID" || echo "INVALID"
[ "${TUNSET:-}" ] && echo "VALID" || echo "INVALID"

Check only, no exit – Only empty is INVALID

And this is the answer to the question.
Use this if you just want to check if the value set/empty=VALID or unset=INVALID.

NOTE, the “1” in “..-1}” is insignificant,
it can be anything (like x)

TSET="good val"
unset TUNSET

if [ "${TSET+1}" ]; then echo "VALID"; else echo "INVALID";fi
if [ "${TEMPTY+1}" ]; then echo "VALID"; else echo "INVALID";fi
if [ "${TUNSET+1}" ]; then echo "VALID"; else echo "INVALID";fi

Short tests

[ "${TSET+1}"   ] && echo "VALID" || echo "INVALID"
[ "${TEMPTY+1}" ] && echo "VALID" || echo "INVALID"
[ "${TUNSET+1}" ] && echo "VALID" || echo "INVALID"

I dedicate this answer to @mklement0 (comments) who challenged me to answer the question accurately.

Reference http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_02



I’m giving a heavily Bash-focused answer because of the bash tag.

Short answer

As long as you’re only dealing with named variables in Bash, this function should always tell you if the variable has been set, even if it’s an empty array.

variable-is-set() {
    declare -p "$1" &>/dev/null

Why this works

In Bash (at least as far back as 3.0), if var is a declared/set variable, then declare -p var outputs a declare command that would set variable var to whatever its current type and value are, and returns status code 0 (success). If var is undeclared, then declare -p var outputs an error message to stderr and returns status code 1. Using &>/dev/null, redirects both regular stdout and stderr output to /dev/null, never to be seen, and without changing the status code. Thus the function only returns the status code.

Why other methods (sometimes) fail in Bash

  • [ -n "$var" ]: This only checks if ${var[0]} is nonempty. (In Bash, $var is the same as ${var[0]}.)
  • [ -n "${var+x}" ]: This only checks if ${var[0]} is set.
  • [ "${#var[@]}" != 0 ]: This only checks if at least one index of $var is set.

When this method fails in Bash

This only works for named variables (including $_), not certain special variables ($!, [email protected], $#, $$, $*, $?, $-, $0, $1, $2, …, and any I may have forgotten). Since none of these are arrays, the POSIX-style [ -n "${var+x}" ] works for all of these special variables. But beware of wrapping it in a function since many special variables change values/existence when functions are called.

Shell compatibility note

If your script has arrays and you’re trying to make it compatible with as many shells as possible, then consider using typeset -p instead of declare -p. I’ve read that ksh only supports the former, but haven’t been able to test this. I do know that Bash 3.0+ and Zsh 5.5.1 each support both typeset -p and declare -p, differing only in which one is an alternative for the other. But I haven’t tested differences beyond those two keywords, and I haven’t tested other shells.

If you need your script to be POSIX sh compatible, then you can’t use arrays. Without arrays, [ -n "{$var+x}" ] works.

Comparison code for different methods in Bash

This function unsets variable var, evals the passed code, runs tests to determine if var is set by the evald code, and finally shows the resulting status codes for the different tests.

I’m skipping test -v var, [ -v var ], and [[ -v var ]] because they yield identical results to the POSIX standard [ -n "${var+x}" ], while requiring Bash 4.2+. I’m also skipping typeset -p because it’s the same as declare -p in the shells I’ve tested (Bash 3.0 thru 5.0, and Zsh 5.5.1).

is-var-set-after() {
    # Set var by passed expression.
    unset var
    eval "$1"

    # Run the tests, in increasing order of accuracy.
    [ -n "$var" ] # (index 0 of) var is nonempty
    [ -n "${var+x}" ] # (index 0 of) var is set, maybe empty
    [ "${#var[@]}" != 0 ] # var has at least one index set, maybe empty
    declare -p var &>/dev/null # var has been declared (any type)

    # Show test results.
    printf '%30s: %2s %2s %2s %2s\n' "$1" $nonempty $plus $count $declared

Test case code

Note that test results may be unexpected due to Bash treating non-numeric array indices as “0” if the variable hasn’t been declared as an associative array. Also, associative arrays are only valid in Bash 4.0+.

# Header.
printf '%30s: %2s %2s %2s %2s\n' "test" '-n' '+x' '#@' '-p'
# First 5 tests: Equivalent to setting 'var=foo' because index 0 of an
# indexed array is also the nonindexed value, and non-numerical
# indices in an array not declared as associative are the same as
# index 0.
is-var-set-after "var=foo"                        #  0  0  0  0
is-var-set-after "var=(foo)"                      #  0  0  0  0
is-var-set-after "var=([0]=foo)"                  #  0  0  0  0
is-var-set-after "var=([x]=foo)"                  #  0  0  0  0
is-var-set-after "var=([y]=bar [x]=foo)"          #  0  0  0  0
# '[ -n "$var" ]' fails when var is empty.
is-var-set-after "var=''"                         #  1  0  0  0
is-var-set-after "var=([0]='')"                   #  1  0  0  0
# Indices other than 0 are not detected by '[ -n "$var" ]' or by
# '[ -n "${var+x}" ]'.
is-var-set-after "var=([1]='')"                   #  1  1  0  0
is-var-set-after "var=([1]=foo)"                  #  1  1  0  0
is-var-set-after "declare -A var; var=([x]=foo)"  #  1  1  0  0
# Empty arrays are only detected by 'declare -p'.
is-var-set-after "var=()"                         #  1  1  1  0
is-var-set-after "declare -a var"                 #  1  1  1  0
is-var-set-after "declare -A var"                 #  1  1  1  0
# If 'var' is unset, then it even fails the 'declare -p var' test.
is-var-set-after "unset var"                      #  1  1  1  1

Test output

The test mnemonics in the header row correspond to [ -n "$var" ], [ -n "${var+x}" ], [ "${#var[@]}" != 0 ], and declare -p var, respectively.

                         test: -n +x #@ -p
                      var=foo:  0  0  0  0
                    var=(foo):  0  0  0  0
                var=([0]=foo):  0  0  0  0
                var=([x]=foo):  0  0  0  0
        var=([y]=bar [x]=foo):  0  0  0  0
                       var='':  1  0  0  0
                 var=([0]=''):  1  0  0  0
                 var=([1]=''):  1  1  0  0
                var=([1]=foo):  1  1  0  0
declare -A var; var=([x]=foo):  1  1  0  0
                       var=():  1  1  1  0
               declare -a var:  1  1  1  0
               declare -A var:  1  1  1  0
                    unset var:  1  1  1  1


  • declare -p var &>/dev/null is (100%?) reliable for testing named variables in Bash since at least 3.0.
  • [ -n "${var+x}" ] is reliable in POSIX compliant situations, but cannot handle arrays.
  • Other tests exist for checking if a variable is nonempty, and for checking for declared variables in other shells. But these tests are suited for neither Bash nor POSIX scripts.


To check whether a variable is set with a non-empty value, use [ -n "$x" ], as others have already indicated.

Most of the time, it’s a good idea to treat a variable that has an empty value in the same way as a variable that is unset. But you can distinguish the two if you need to: [ -n "${x+set}" ] ("${x+set}" expands to set if x is set and to the empty string if x is unset).

To check whether a parameter has been passed, test $#, which is the number of parameters passed to the function (or to the script, when not in a function) (see Paul’s answer).


Read the “Parameter Expansion” section of the bash man page. Parameter expansion doesn’t provide a general test for a variable being set, but there are several things you can do to a parameter if it isn’t set.

For example:

function a {
    # rest of the function

will set first_arg equal to $1 if it is assigned, otherwise it uses the value “foo”. If a absolutely must take a single parameter, and no good default exists, you can exit with an error message when no parameter is given:

function a {
    : ${1?a must take a single argument}
    # rest of the function

(Note the use of : as a null command, which just expands the values of its arguments. We don’t want to do anything with $1 in this example, just exit if it isn’t set)


In bash you can use -v inside the [[ ]] builtin:

#! /bin/bash -u

if [[ ! -v SOMEVAR ]]; then



Using [[ -z "$var" ]] is the easiest way to know if a variable was set or not, but that option -z doesn’t distinguish between an unset variable and a variable set to an empty string:

$ set=''
$ [[ -z "$set" ]] && echo "Set" || echo "Unset" 
$ [[ -z "$unset" ]] && echo "Set" || echo "Unset"

It’s best to check it according to the type of variable: env variable, parameter or regular variable.

For a env variable:

[[ $(env | grep "varname=" | wc -l) -eq 1 ]] && echo "Set" || echo "Unset"

For a parameter (for example, to check existence of parameter $5):

[[ $# -ge 5 ]] && echo "Set" || echo "Unset"

For a regular variable (using an auxiliary function, to do it in an elegant way):

function declare_var {
   declare -p "$1" &> /dev/null
declare_var "var_name" && echo "Set" || echo "Unset"


  • $#: gives you the number of positional parameters.
  • declare -p: gives you the definition of the variable passed as a parameter. If it exists, returns 0, if not, returns 1 and prints an error message.
  • &> /dev/null: suppresses output from declare -p without affecting its return code.


The answers above do not work when Bash option set -u is enabled. Also, they are not dynamic, e.g., how to test is variable with name “dummy” is defined? Try this:

    if [ $# -ne 1 ]
        echo "Expected exactly one argument: variable name as string, e.g., 'my_var'"
        exit 1
    # Tricky.  Since Bash option 'set -u' may be enabled, we cannot directly test if a variable
    # is defined with this construct: [ ! -z "$var" ].  Instead, we must use default value
    # substitution with this construct: [ ! -z "${var:-}" ].  Normally, a default value follows the
    # operator ':-', but here we leave it blank for empty (null) string.  Finally, we need to
    # substitute the text from $1 as 'var'.  This is not allowed directly in Bash with this
    # construct: [ ! -z "${$1:-}" ].  We need to use indirection with eval operator.
    # Example: $1="var"
    # Expansion for eval operator: "[ ! -z ${$1:-} ]" -> "[ ! -z ${var:-} ]"
    # Code  execute: [ ! -z ${var:-} ]
    eval "[ ! -z ${$1:-} ]"
    return $?  # Pedantic.

Related: In Bash, how do I test if a variable is defined in "-u" mode


You can do:

function a {
        if [ ! -z "$1" ]; then
                echo '$1 is set'


My preferred way is this:

$ var=10
$ if ! ${var+false};then echo "is set";else echo "NOT set";fi
is set
$ unset -v var
$ if ! ${var+false};then echo "is set";else echo "NOT set";fi
NOT set

So basically, if a variable is set, it becomes “a negation of the resulting false” (what will be true = “is set”).

And, if it is unset, it will become “a negation of the resulting true” (as the empty result evaluates to true) (so will end as being false = “NOT set”).


[[ $foo ]]


(( ${#foo} ))


let ${#foo}


declare -p foo


In a shell you can use the -z operator which is True if the length of string is zero.

A simple one-liner to set default MY_VAR if it’s not set, otherwise optionally you can display the message:

[[ -z "$MY_VAR" ]] && MY_VAR="default"
[[ -z "$MY_VAR" ]] && MY_VAR="default" || echo "Variable already set."


if [[ ${1:+isset} ]]
then echo "It was set and not null." >&2
else echo "It was not set or it was null." >&2

if [[ ${1+isset} ]]
then echo "It was set but might be null." >&2
else echo "It was was not set." >&2


I found a (much) better code to do this if you want to check for anything in [email protected].

if [[ $1 = "" ]]
  echo '$1 is blank'
  echo '$1 is filled up'

Why this all? Everything in [email protected] exists in Bash, but by default it’s blank, so test -z and test -n couldn’t help you.

Update: You can also count number of characters in a parameters.

if [ ${#1} = 0 ]
  echo '$1 is blank'
  echo '$1 is filled up'


if [[ ${!xx[@]} ]] ; then echo xx is defined; fi


This is what I use every day:

# Check if a variable is set
#   param1  name of the variable
function is_set()
    [[ -n "" ]] && test -n "$(eval "echo "${+x}"")"

This works well under Linux and Solaris down to bash 3.0.

bash-3.00$ myvar="TEST"
bash-3.00$ is_set myvar ; echo $?
bash-3.00$ mavar=""
bash-3.00$ is_set myvar ; echo $?
bash-3.00$ unset myvar
bash-3.00$ is_set myvar ; echo $?


I like auxiliary functions to hide the crude details of bash. In this case, doing so adds even more (hidden) crudeness:

# The first ! negates the result (can't use -n to achieve this)
# the second ! expands the content of varname (can't do ${$varname})
function IsDeclared_Tricky
  local varname="$1"
  ! [ -z ${!varname+x} ]

Because I first had bugs in this implementation (inspired by the answers of Jens and Lionel), I came up with a different solution:

# Ask for the properties of the variable - fails if not declared
function IsDeclared()
  declare -p $1 &>/dev/null

I find it to be more straight-forward, more bashy and easier to understand/remember. Test case shows it is equivalent:

function main()
  declare -i xyz
  local foo
  local bar=
  local baz=''

  IsDeclared_Tricky xyz; echo "IsDeclared_Tricky xyz: $?"
  IsDeclared_Tricky foo; echo "IsDeclared_Tricky foo: $?"
  IsDeclared_Tricky bar; echo "IsDeclared_Tricky bar: $?"
  IsDeclared_Tricky baz; echo "IsDeclared_Tricky baz: $?"

  IsDeclared xyz; echo "IsDeclared xyz: $?"
  IsDeclared foo; echo "IsDeclared foo: $?"
  IsDeclared bar; echo "IsDeclared bar: $?"
  IsDeclared baz; echo "IsDeclared baz: $?"


The test case also shows that local var does NOT declare var (unless followed by ‘=’). For quite some time I thought i declared variables this way, just to discover now that i merely expressed my intention… It’s a no-op, i guess.

IsDeclared_Tricky xyz: 1
IsDeclared_Tricky foo: 1
IsDeclared_Tricky bar: 0
IsDeclared_Tricky baz: 0
IsDeclared xyz: 1
IsDeclared foo: 1
IsDeclared bar: 0
IsDeclared baz: 0

BONUS: usecase

I mostly use this test to give (and return) parameters to functions in a somewhat “elegant” and safe way (almost resembling an interface…):

#auxiliary functions
function die()
  echo "Error: $1"; exit 1

function assertVariableDeclared()
  IsDeclared "$1" || die "variable not declared: $1"

function expectVariables()
  while (( $# > 0 )); do
    assertVariableDeclared $1; shift

# actual example
function exampleFunction()
  expectVariables inputStr outputStr
  outputStr="$inputStr world!"

function bonus()
  local inputStr='Hello'
  local outputStr= # remove this to trigger error
  echo $outputStr


If called with all requires variables declared:

Hello world!


Error: variable not declared: outputStr


After skimming all the answers, this also works:

if [[ -z $SOME_VAR ]]; then read -p "Enter a value for SOME_VAR: " SOME_VAR; fi

if you don’t put SOME_VAR instead of what I have $SOME_VAR, it will set it to an empty value; $ is necessary for this to work.


To check if a var is set or not

var=""; [[ $var ]] && echo "set" || echo "not set"


If you wish to test that a variable is bound or unbound, this works well, even after you’ve turned on the nounset option:

set -o noun set

if printenv variableName >/dev/null; then
    # variable is bound to a value
    # variable is unbound


I always use this one, based on the fact that it seems easy to be understood by anybody who sees the code for the very first time:

if [ "$variable" = "" ]
    echo "Variable X is empty"

And, if wanting to check if not empty;

if [ ! "$variable" = "" ]
    echo "Variable X is not empty"

That’s it.