Differences between revisions 19 and 28 (spanning 9 versions)
Revision 19 as of 2014-08-25 14:09:00
Size: 4210
Editor: geirha
Comment: I don't see the point in testing typeset/declare's output
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.

{{{
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.
}}}

=== Testing that a variable has been declared ===

Distinguishing between a variable that is ''undefined'' and one that is ''defined but empty'' is somewhat trickier, but possible.
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 42: Line 7:
${var+:} false
! ${var+false}
[ -n "${var+_}" ]
[ "${var+_}" ]
if test "$var"; then
  echo "The variable has a non-empty value."
fi
Line 48: Line 12:
Which of the above performs best varies from shell to shell. If POSIX isn't a requirement, the following `[[` solution usually performs best. If this fails for you because you use `set -u`, please see [[BashFAQ/112|FAQ 112]].

If we wish to distinguish between an ''empty'' variable and an ''unset'' variable, then we may use the `+` [[BashFAQ/073|parameter expansion]]:
Line 51: Line 17:
# Bash/ksh
[[ ${var+_} ]]
# POSIX
if test "${var+defined}"; then
  echo "The variable is defined."
fi
Line 55: Line 23:
Another method that's occasionally seen is to check the status of `typeset -p`. The above method should be preferred over this. 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 58: Line 28:
# Bash/ksh/zsh
typeset -p var >/dev/null 2>&1
# returns 0 if var exists, error otherwise
# 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 63: Line 35:
Bash 4.2 adds a `-v` test: 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 66: Line 42:
# Bash 4.2 / ksh93
if [[ -v var ]]; then echo "var is defined"; fi
# POSIX
: "${var=default}"
Line 70: Line 46:
ksh93 additionally supports array indices with the `-v` test, while Bash so far does not. This should be the preferred method when targeting ksh93 specifically. See [[BashFAQ/073|FAQ 73]] for details.
Line 72: Line 48:
=== Testing that a function has been defined === === Testing whether a function has been defined ===
Line 74: Line 50:
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 77: Line 53:
declare -F f >/dev/null  # Bash only - outputs "f" and returns true if defined, otherwise false
typeset -f f >/dev/null  # Bash/Ksh - typeset returns false if undefined, otherwise outputs the entire function and returns true.
[[ $(type -t f) == function ]] # Bash-only - "type" outputs "function" if defined. In ksh (and mksh), the "type" alias for "whence -v" differs.
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.
Line 82: 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)