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 [ -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 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.
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.
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 | FOO="world" | 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 | "" |
+--------------------+----------------------+-----------------+-----------------+
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
.
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).
Headings:
VARIABLE
is non-empty (VARIABLE="something"
)VARIABLE
is empty/null (VARIABLE=""
)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"
.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
I'm giving a heavily Bash-focused answer because of the bash
tag.
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
}
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.
[ -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.
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.
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.
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
}
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
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.
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
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"
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 http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_02
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.
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)
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).
Use test -n "${var-}"
to check if the variable is not empty (and hence must be defined/set too). Usage:
if test -n "${name-}"; then
echo "name is set to $name"
else
echo "name is not set or empty"
fi
Use test ! -z "${var+}"
to check if the variable is defined/set (even if it's empty). Usage:
if test ! -z "${var+}"; then
echo "name is set to $name"
else
echo "name 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.
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 $a
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.
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 fromdeclare -p
without affecting its return code.
To clearly answer 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 |
+----------------------+-------------+---------+------------+
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
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").
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.
bash-3.00$ myvar="TEST"
bash-3.00$ is_set myvar ; echo $?
0
bash-3.00$ myvar=
bash-3.00$ is_set myvar ; echo $?
0
bash-3.00$ unset myvar
bash-3.00$ is_set myvar ; echo $?
1
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
printf "$R" "\${FOO:?x}" "${S:?x}" "<error>" "<error> "; S='V';N='';unset U
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" "----------" "-------" "-------" "---------";
# For reference, the following two lines are the literal rows of the table
# which would cause the script to exit and so have had <error> written in.
#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
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 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."
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 recommand its usage, but this implies that the difference between an unset variable and a variable set with an empty string is handled correctly.
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
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 error
exampleFunction
echo $outputStr
}
bonus
If called with all requires variables declared:
Hello world!
else:
Error: variable not declared: outputStr
if test $# -gt 0; then printf 'arg <%s>\n' "$@"; fi
. – Jens-v
test, although this is seemingly only available on new versions ofbash
and not portable across shells. – Graeme