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 ?
}
if test $# -gt 0; then printf 'arg <%s>\n' "$@"; fi
.
-v
test, although this is seemingly only available on new versions of bash
and not portable across shells.
(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!"
else
echo "First parameter not supplied."
fi
[[ ]]
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.
[]
works better especially for shell (non-bash) scripts.
-n
is the default test, so more simply [ "$1" ]
or [[ $1 ]]
work as well. Also note that with [[ ]]
quoting is unnecessary.
if [ ! -z "$1" ]
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 | "" |
+--------------------+----------------------+-----------------+-----------------+
set foo; echo ${1:-set but null or unset}
echos "foo"; set --; echo ${1:-set but null or unset}
echoes set but null ...
set
:-)
: ${FOOBAR:=/etc/foobar.conf}
to set a default value for a variable if it is unset or null.
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}
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
foo=bar
[[ -v foo ]]; echo $?
# 0
foo=""
[[ -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
.
[[ -v aaa ]]; echo $?
==> -bash: conditional binary operator expected
-bash: syntax error near 'aaa'
[[ -v foo ]]
and NOT [[ -v $foo ]]
if you don't want to waste half an hour staring at your screen wondering what's going on. In addition to verifying it is set, you are usually also looking for a particular value, say 123
. In that case I prefer to use [[ ${foo-123} -eq 123 ]]
to condense [[ -v foo && $foo -eq 123 ]]
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.
[[ ]]
is nothing but a portability pitfall. if [ -n "$1" ]
should be used here.
foo=""
, $foo
has a value, but -z
will merely report that it's empty. And -z $foo
will blow up if you have set -o nounset
.
foo=
), then Russel's answer is correct.
I always find the POSIX table in the other answer slow to grok, so here's my take on it:
parameter expansion VARIABLE set VARIABLE empty VARIABLE 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).
Headings:
set means VARIABLE is non-empty (VARIABLE="something")
empty means VARIABLE is empty/null (VARIABLE="")
unset means VARIABLE does not exist (unset VARIABLE)
Values:
$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 "default" and that VARIABLE (previously empty or unset) will also be set equal to "default".
VARIABLE=""
could be written as VARIABLE=
. Still, the former is more readable.
VARIABLE
is unset, I'm seeing that A=${VARIABLE=default}
has A
equal to "default"
, rather than the original value of VARIABLE
(which was unset). Is your description of $(VARIABLE="default")
correct? Perhaps I am misreading what you mean by original value.
$(VARIABLE="default")
means the result is "default"
and that VARIABLE
will be set equal to "default"
too.
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
[ $isMac ] && param="--enable-shared"
${variable+x}
to get x
iff $variable
is set) almost half a year earlier. Also it has much more detail explaining why it's right.
Note
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 ($!
, $@
, $#
, $$
, $*
, $?
, $-
, $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
, eval
s the passed code, runs tests to determine if var
is set by the eval
d 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
nonempty=$?
[ -n "${var+x}" ] # (index 0 of) var is set, maybe empty
plus=$?
[ "${#var[@]}" != 0 ] # var has at least one index set, maybe empty
count=$?
declare -p var &>/dev/null # var has been declared (any type)
declared=$?
# 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
Summary
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.
declare -p var &>/dev/null
) is the ONLY one which works. THANK YOU!
declare -p PATH &> /dev/null
returns 0 where as declare -p ASDFASDGFASKDF &> /dev/null
returns 1. (on bash 5.0.11(1)-release)
declare var
declares var a variable but it does not set it in terms of set -o nounset
: Test with declare foo bar=; set -o nounset; echo $bar; echo $foo
/
! I fixed that, but I think the logic is confusing enough to warrant a function, especially in light of @xebeche's comment. @xebeche That's inconvenient for my answer.… It looks like I'll have to try something like declare -p "$1" | grep =
or test -v "$1"
.
set -o nounset; (echo "$var") &>/dev/null
has the same truth value as [ -n "${var+x}" ]
, so the only difference is set -o nounset
causing a script/subshell to exit. That said, I agree that my answer should explain the difference between a variable being set vs merely declared instead of conflating these cases. Feel free to edit. I'm not returning to this answer until the next perennial notification.
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
then
echo "Variable is unset"
elif [ -z "$SOMEVARIABLE" ]
then
echo "Variable is set to an empty string"
else
echo "Variable is set to some string"
fi
[ -v "$VARNAME" ]
is not incorrect, but simply performs a different test. Suppose VARNAME=foo
; then it checks if there is a variable named foo
that is set.
-v
is not POSIX compliant
[: -v: unexpected operator
, one has to ensure to use bash
instead of sh
.
if [ "$1" != "" ]; then
echo \$1 is set
else
echo \$1 is not set
fi
Although for arguments it is normally best to test $#, which is the number of arguments, in my opinion.
if [ $# -gt 0 ]; then
echo \$1 is set
else
echo \$1 is not set
fi
[ "$1" != "" ]
(or [ -n "$1" ]
)...
$1
is set to the empty string.
$1
is set to an empty string.
set -o nounset
is on.
Summary
Use test -n "${var-}" to check if the variable is not empty (and hence must be defined/set too). Usage: if test -n "${var-}"; then echo "var is set to <$var>" else echo "var is not set or empty" fi
Use test -n "${var+x}" to check if the variable is defined/set (even if it's empty). Usage: if test -n "${var+x}"; then echo "var is set to <$var>" else echo "var is not set" fi
Note that the first use case is much more common in shell scripts and this is what you will usually want to use.
Notes
This solution should work in all POSIX shells (sh, bash, zsh, ksh, dash)
Some of the other answers for this question are correct but may be confusing for people unexperienced in shell scripting, so I wanted to provide a TLDR answer that will be least confusing for such people.
Explanation
To understand how this solution works, you need to understand the POSIX test
command and POSIX shell parameter expansion (spec), so let's cover the absolute basics needed to understand the answer.
The test command evaluates an expression and returns true or false (via its exit status). The operator -n
returns true if the operand is a non-empty string. So for example, test -n "a"
returns true, while test -n ""
returns false. Now, to check if a variable is not empty (which means it must be defined), you could use test -n "$var"
. However, some shell scripts have an option set (set -u
) that causes any reference to undefined variables to emit an error, so if the variable var
is not defined, the expression $var
will cause an error. To handle this case correctly, you must use variable expansion, which will tell the shell to replace the variable with an alternative string if it's not defined, avoiding the aforementioned error.
The variable expansion ${var-}
means: if the variable var
is undefined (also called "unset"), replace it with an empty string. So test -n "${var-}"
will return true if $var
is not empty, which is almost always what you want to check in shell scripts. The reverse check, if $var
is undefined or not empty, would be test -z "${var-}"
.
Now to the second use case: checking if the variable var
is defined, whether empty or not. This is a less common use case and slightly more complex, and I would advise you to read Lionels's great answer to better understand it.
test -z
. From test's man page: -z STRING returns true if the length of STRING is zero. So, test -z "${var-}"
will return true if var is undefined or empty.
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
peek@peek:~$ ./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"
TEMPTY=""
unset TUNSET
if [ "${TSET:-}" ]; then echo "VALID"; else echo "INVALID";fi
# VALID
if [ "${TEMPTY:-}" ]; then echo "VALID"; else echo "INVALID";fi
# INVALID
if [ "${TUNSET:-}" ]; then echo "VALID"; else echo "INVALID";fi
# INVALID
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"
TEMPTY=""
unset TUNSET
if [ "${TSET+1}" ]; then echo "VALID"; else echo "INVALID";fi
# VALID
if [ "${TEMPTY+1}" ]; then echo "VALID"; else echo "INVALID";fi
# VALID
if [ "${TUNSET+1}" ]; then echo "VALID"; else echo "INVALID";fi
# INVALID
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: 2.6.2 Parameter Expansion
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
fi
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.
${var:-}
, not ${var-}
. But in Bash, I can confirm it works even without the colon.
${var:-}
and ${var-}
mean different things. ${var:-}
will expand to empty string if var
unset or null and ${var-}
will expand to empty string only if unset.
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 {
first_arg=${1-foo}
# 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)
: ${var?message}
.
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).
I'm surprised nobody has tried to write a shell script to programmatically generate the infamously hard to grok table. Since we're here trying to learn coding techniques, why not express the answer in code? :) Here's my take (should work in any POSIX shell):
H="+-%s-+-%s----+-%s----+-%s--+\n" # table divider printf format
R="| %-10s | %-10s | %-10s | %-10s |\n" # table row printf format
S='V' # S is a variable that is set-and-not-null
N='' # N is a variable that is set-but-null (empty "")
unset U # U is a variable that is unset
printf "$H" "----------" "-------" "-------" "---------";
printf "$R" "expression" "FOO='V'" "FOO='' " "unset FOO";
printf "$H" "----------" "-------" "-------" "---------";
printf "$R" "\${FOO:-x}" "${S:-x}" "${N:-x}" "${U:-x} "; S='V';N='';unset U
printf "$R" "\${FOO-x} " "${S-x} " "${N-x} " "${U-x} "; S='V';N='';unset U
printf "$R" "\${FOO:=x}" "${S:=x}" "${N:=x}" "${U:=x} "; S='V';N='';unset U
printf "$R" "\${FOO=x} " "${S=x} " "${N=x} " "${U=x} "; S='V';N='';unset U
# "${N:?x}" "${U:?x} "
printf "$R" "\${FOO:?x}" "${S:?x}" "<error>" "<error> "; S='V';N='';unset U
# "${U?x} "
printf "$R" "\${FOO?x} " "${S?x} " "${N?x} " "<error> "; S='V';N='';unset U
printf "$R" "\${FOO:+x}" "${S:+x}" "${N:+x}" "${U:+x} "; S='V';N='';unset U
printf "$R" "\${FOO+x} " "${S+x} " "${N+x} " "${U+x} "; S='V';N='';unset U
printf "$H" "----------" "-------" "-------" "---------";
And the output of running the script:
+------------+------------+------------+------------+
| expression | FOO='V' | FOO='' | unset FOO |
+------------+------------+------------+------------+
| ${FOO:-x} | V | x | x |
| ${FOO-x} | V | | x |
| ${FOO:=x} | V | x | x |
| ${FOO=x} | V | | x |
| ${FOO:?x} | V | <error> | <error> |
| ${FOO?x} | V | | <error> |
| ${FOO:+x} | x | | |
| ${FOO+x} | x | x | |
+------------+------------+------------+------------+
The script is missing a few features like displaying when the side-effect assignments do (or do not) take place, but maybe some other more ambitious person wants to take this starting point and run with it.
In Bash you can use -v
inside the [[ ]]
builtin:
#! /bin/bash -u
if [[ ! -v SOMEVAR ]]; then
SOMEVAR='hello'
fi
echo $SOMEVAR
To clearly answer the OP's question of how to determine whether a variable is set, Lionel's answer is correct:
if test "${name+x}"; then
echo 'name is set'
else
echo 'name is not set'
fi
This question already has a lot of answers, but none of them offered bona fide Boolean expressions to clearly differentiate between variables values.
Here are some unambiguous expressions that I worked out:
+-----------------------+-------------+---------+------------+
| Expression in script | name='fish' | name='' | unset name |
+-----------------------+-------------+---------+------------+
| test "$name" | TRUE | f | f |
| test -n "$name" | TRUE | f | f |
| test ! -z "$name" | TRUE | f | f |
| test ! "${name-x}" | f | TRUE | f |
| test ! "${name+x}" | f | f | TRUE |
+-----------------------+-------------+---------+------------+
By the way, these expressions are equivalent: test <expression>
<=> [ <expression> ]
Other ambiguous expressions to be used with caution:
+----------------------+-------------+---------+------------+
| Expression in script | name='fish' | name='' | unset name |
+----------------------+-------------+---------+------------+
| test "${name+x}" | TRUE | TRUE | f |
| test "${name-x}" | TRUE | f | TRUE |
| test -z "$name" | f | TRUE | TRUE |
| test ! "$name" | f | TRUE | TRUE |
| test ! -n "$name" | f | TRUE | TRUE |
| test "$name" = '' | f | TRUE | TRUE |
+----------------------+-------------+---------+------------+
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"
Unset
$ [[ -z "$unset" ]] && echo "Set" || echo "Unset"
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"
Notes:
$#: 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.
You can do:
function a {
if [ ! -z "$1" ]; then
echo '$1 is set'
fi
}
-n
and not negate the -z
.
$1
, i.e., [ ! -z "$1" ]
. Or you can write [[ ! -z $1 ]]
in bash/ksh/zsh.
$1
is set to the empty string.
To test if a variable var
is set: [ ${var+x} ]
.
To test if a variable is set by name: [ ${!name+x} ]
.
To test if a positional parameter is set: [ ${N+x} ]
, where N is actually an integer.
This answer is almost similar to Lionel’s but explore a more minimalist take by omitting the -z
.
To test if a named variable is set:
function is_set {
local v=$1
echo -n "${v}"
if [ ${!v+x} ]; then
echo " = '${!v}'"
else
echo " is unset"
fi
}
To test if a positional parameter is set:
function a {
if [ ${1+x} ]; then
local arg=$1
echo "a '${arg}'"
else
echo "a: arg is unset"
fi
}
Testing shows that extra care with white spaces and valid test expressions is not needed.
set -eu
V1=a
V2=
V4=-gt
V5="1 -gt 2"
V6="! -z 1"
V7='$(exit 1)'
is_set V1
is_set V2
is_set V3
is_set V4
is_set V5
is_set V6
is_set V7
a 1
a
a "1 -gt 2"
a 1 -gt 2
$ ./test.sh
V1 = 'a'
V2 = ''
V3 is unset
V4 = '-gt'
V5 = '1 -gt 2'
V6 = '! -z 1'
V7 = '$(exit 1)'
a '1'
a: arg is unset
a '1 -gt 2'
a '1'
Finally, notice the set -eu
which protects us from common errors, such as typos in variable names. I recommend its usage, but this implies that the difference between an unset variable and a variable set with an empty string is handled correctly.
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:
is_var_defined()
{
if [ $# -ne 1 ]
then
echo "Expected exactly one argument: variable name as string, e.g., 'my_var'"
exit 1
fi
# 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
eval "[ ! -z \${$1:-} ]"
to indirect evaluation: [ ! -z ${!1:-} ];
.
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").
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."
This is what I use every day:
#
# Check if a variable is set
# param1 name of the variable
#
function is_set() { [[ $(eval echo "\${${1}+x}") ]]; }
This works well under Linux and Solaris down to Bash 3.0.
$ myvar="TEST"
$ is_set myvar ; echo $?
0
$ myvar=
$ is_set myvar ; echo $?
0
$ unset myvar
$ is_set myvar ; echo $?
1
test -n "$(eval "echo "\${${1}+x}"")"
for the equivalent: [[ ${!1+x} ]]
eval
is evil but this works with all the versions of bash and zsh I have had to use.
[[ $foo ]]
Or
(( ${#foo} ))
Or
let ${#foo}
Or
declare -p foo
I found a (much) better code to do this if you want to check for anything in $@
.
if [[ $1 = "" ]] then echo '$1 is blank' else echo '$1 is filled up' fi
Why this all? Everything in $@
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 ] then echo '$1 is blank' else echo '$1 is filled up' fi
if [[ ${!xx[@]} ]] ; then echo xx is defined; fi
After skimming all the answers, this also works:
if [[ -z $SOME_VAR ]]; then read -p "Enter a value for SOME_VAR: " SOME_VAR; fi
echo "SOME_VAR=$SOME_VAR"
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.
set -u
in effect which will print the unboud variable
error
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: $?"
}
main
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
done
}
# Actual example
function exampleFunction()
{
expectVariables inputStr outputStr
outputStr="$inputStr, World!"
}
function bonus()
{
local inputStr='Hello'
local outputStr= # Remove this to trigger the error
exampleFunction
echo $outputStr
}
bonus
If called with all required variables declared:
Hello, World!
else:
Error: variable not declared: outputStr
Success story sharing
${var+x}
is the correct substitution to use. Using[ -z ${var:+x} ]
produces no different result than[ -z "$var" ]
.[ -z "" -a -z ${var+x} ]
getsbash: [: 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.[ -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.