Differences between revisions 22 and 28 (spanning 6 versions)
Revision 22 as of 2015-04-06 17:17:02
Size: 4348
Editor: ormaaj
Comment: -v with arrays in bash 4.3
Revision 28 as of 2022-11-26 06:06:57
Size: 2489
Editor: emanuele6
Comment: ${x:+str} => ${x:+'str'}
Deletions are marked like this. Additions are marked like this.
Line 3: Line 3:
There are several ways to test for a defined or non-empty variable or function depending upon the requirements.

<<TableOfContents>>

=== "Declared", "Defined", and "Undefined" ===

Like in many languages, ''declaration'' and ''definition'' can be independent steps. To declare an object is to give it a name and datatype, whereas a definition associates it with a value, which may be empty. In Unix shell terminology, "unset", "set", and "null" are often used to mean "undefined", "defined", and "empty" respectively. Since "string" is very nearly the only common datatype, "null" usually means the empty string. With arrays, things become less consistent between shells.

=== Testing for a defined, non-empty variable / parameter. ===

Since the most common goal is to distinguish between an empty and non-empty variable or parameter, we'll discuss it first. These are the most common solutions arranged from most to least portable.

|| `test x"$var" != x` || Only needed for ancient Bourne shells. Don't use this in modern scripts. ||
|| `test -n "$var"` || POSIX sh plus some older Bourne shells which lacked `[`. ||
|| `[ -n "$var" ]` || POSIX sh and most Bourne shells including Heirloom. Best overall choice for new scripts requiring POSIX compatibility. ||
|| `test "$var"` || POSIX sh. POSIX specifies that for one argument, non-null is always true while null is always false. ||
|| `[ "$var" ]` || POSIX sh. As above. Still very portable. ||
|| `[[ -n $var ]]` || Here and below is Bash/Ksh/Zsh only. This is compatible with some buggy shell versions which didn't process the implicit -n correctly. ||
|| `[[ $var ]]` || Best overall choice for Bash/Ksh where POSIX isn't a requirement. Usually best performance. This won't work in Zsh. ||
|| `(( ${#var} ))` || About as portable as the above. Almost always slower. Don't use this to test for empty strings. ||

To check the converse, whether a variable is empty (unset or zero-length), invert the above logic.
There are several ways to test these things, depending on the exact requirements. Most of the time, the desired test is ''whether a variable has a non-empty value''. In this case, we may simply use:
Line 27: Line 6:
test x"$var" = x
test -z "$var"
[ -z "$var" ]
test ! "$var"
[ ! "$var" ]
[[ -z $var ]]
[[ ! $var ]] # Also won't work in Zsh in any emulate mode. If such portability is needed, adjust solutions on this page to use -z or -n explicitly.
# POSIX
if test "$var"; then
  echo "The variable has a non-empty value."
fi
Line 36: Line 12:
=== Testing that a variable has been declared === If this fails for you because you use `set -u`, please see [[BashFAQ/112|FAQ 112]].
Line 38: Line 14:
Distinguishing between a variable that is ''undefined'' and one that is ''defined but empty'' is somewhat trickier, but possible. If we wish to distinguish between an ''empty'' variable and an ''unset'' variable, then we may use the `+` [[BashFAQ/073|parameter expansion]]:
Line 41: Line 17:
# POSIX. Not Bourne-compatible.
! ${var+false} # Usually fastest and cleanest.
[ "$((${var+1}))" -ne 0 ]

# Bourne / POSIX
${var+:} false
[ -n "${var+_}" ]
[ "${var+_}" ]
# POSIX
if test "${var+defined}"; then
  echo "The variable is defined."
fi
Line 51: Line 23:
Which of the above performs best varies from shell to shell. If POSIX isn't a requirement, the following `[[` solution usually performs best. The magic here is the `+`, ''not'' the word `defined`. We can use any non-empty word after the `+` sign. I prefer `defined` because it indicates what kind of test is being performed.

Some people prefer the `-v` option that was added in bash 4.2:
Line 54: Line 28:
# Bash/ksh
[[ ${var+_} ]]
(( ${var+1} ))
# Bash 4.2 and up
# Bash 4.3 if you want to test an array element
if test -v var; then
  echo "The variable is defined."
fi
Line 59: Line 35:
Another method that's occasionally seen is to check the status of `typeset -p`. The above method should be preferred over this. There's really no benefit to this over the portable test, though.

=== Setting a default value ===

If what we really want is to set a variable to a default value ''unless it already has a value'', then we may skip the test, and use the `=` parameter expansion:
Line 62: Line 42:
# Bash/ksh/zsh
typeset -p var >/dev/null 2>&1
# returns 0 if var exists, error otherwise
# POSIX
: "${var=default}"
Line 67: Line 46:
Bash 4.2 adds a `-v` test: See [[BashFAQ/073|FAQ 73]] for details.
Line 69: Line 48:
{{{
# Bash 4.2 / ksh93
if [[ -v var ]]; then echo "var is defined"; fi
}}}
=== Testing whether a function has been defined ===
Line 74: Line 50:
ksh93 and bash (since 4.3) support array indices with the `-v` test. Bash 4.2 did not. This is the "best" but also least portable method to test for an unset variable.

=== Testing that a function has been defined ===

For determining whether a function with a given name is already defined, there are several answers, all of which require Bash (or at least, non-Bourne) commands. Testing that a function is defined should rarely be necessary.
For determining whether a function with a given name is already defined, there are several answers, all of which require Bash (or at least, non-Bourne) commands. Testing that a function is defined should rarely be necessary. Just define the function as you want it to be defined instead of worrying about what might or might not have been inherited from who-knows-where.
Line 86: Line 58:
isFunction() [[ $(type ${BASH_VERSION:+-t} "$1") == ${KSH_VERSION:+"$1 is a "}function ]]; isFunction f isFunction() [[ $(type ${BASH_VERSION:+'-t'} "$1") == ${KSH_VERSION:+"$1 is a "}function ]]; isFunction f

How do I determine whether a variable is already defined? Or a function?

There are several ways to test these things, depending on the exact requirements. Most of the time, the desired test is whether a variable has a non-empty value. In this case, we may simply use:

# POSIX
if test "$var"; then
  echo "The variable has a non-empty value."
fi

If this fails for you because you use set -u, please see FAQ 112.

If we wish to distinguish between an empty variable and an unset variable, then we may use the + parameter expansion:

# POSIX
if test "${var+defined}"; then
  echo "The variable is defined."
fi

The magic here is the +, not the word defined. We can use any non-empty word after the + sign. I prefer defined because it indicates what kind of test is being performed.

Some people prefer the -v option that was added in bash 4.2:

# Bash 4.2 and up
# Bash 4.3 if you want to test an array element
if test -v var; then
  echo "The variable is defined."
fi

There's really no benefit to this over the portable test, though.

Setting a default value

If what we really want is to set a variable to a default value unless it already has a value, then we may skip the test, and use the = parameter expansion:

# POSIX
: "${var=default}"

See FAQ 73 for details.

Testing whether a function has been defined

For determining whether a function with a given name is already defined, there are several answers, all of which require Bash (or at least, non-Bourne) commands. Testing that a function is defined should rarely be necessary. Just define the function as you want it to be defined instead of worrying about what might or might not have been inherited from who-knows-where.

declare -F f >/dev/null       # Bash only - declare outputs "f" and returns 0 if defined, returns non-zero otherwise.
typeset -f f >/dev/null       # Bash/Ksh - typeset outputs the entire function and returns 0 if defined, returns non-zero otherwise.
[[ $(type -t f) = function ]] # Bash-only - "type" outputs "function" if defined. In ksh (and mksh), the "type" alias for "whence -v" differs.

# Bash/Ksh. Workaround for the above, but the first two are preferable.
isFunction() [[ $(type ${BASH_VERSION:+'-t'} "$1") == ${KSH_VERSION:+"$1 is a "}function ]]; isFunction f

BashFAQ/083 (last edited 2022-11-26 06:06:57 by emanuele6)