2673
Comment: Documented converse: -z instead of -n, otherwise added !.
|
4210
I don't see the point in testing typeset/declare's output
|
Deletions are marked like this. | Additions are marked like this. |
Line 3: | Line 3: |
There are several ways to determine whether a variable is defined to have a non-empty value. Here are the most common ones, in order from most portable to least portable: | There are several ways to test for a defined or non-empty variable or function depending upon the requirements. |
Line 5: | Line 5: |
{{{ test x"$var" != x test -n "$var" [ -n "$var" ] test "$var" [ "$var" ] [[ -n $var ]] [[ $var ]] }}} |
<<TableOfContents>> |
Line 15: | Line 7: |
To check the converse, whether a variable is empty (unset or zero-length), you can use: | === "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. |
Line 22: | Line 31: |
[ "$var" ] | [ ! "$var" ] |
Line 24: | Line 33: |
[[ ! $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. |
Line 27: | Line 36: |
If you need to distinguish between a variable that is ''undefined'' and one that is ''defined but empty'', then it becomes much trickier. There is no explicit shell command to test for existence of a variable (until bash 4.2), but there are some tricks that can be used. With older bash releases, one way is to use "declare": | === 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. |
Line 30: | Line 41: |
# Bash declare -p var >/dev/null 2>&1 |
# POSIX ${var+:} false ! ${var+false} [ -n "${var+_}" ] [ "${var+_}" ] }}} Which of the above performs best varies from shell to shell. If POSIX isn't a requirement, the following `[[` solution usually performs best. {{{ # Bash/ksh [[ ${var+_} ]] }}} Another method that's occasionally seen is to check the status of `typeset -p`. The above method should be preferred over this. {{{ # Bash/ksh/zsh typeset -p var >/dev/null 2>&1 |
Line 34: | Line 62: |
Here's another one that uses [[BashFAQ/073|parameter expansion]]: {{{ # Bourne test "${var+defined}" }}} This expansion results in nothing if var is undefined. Therefore test returns false. If var is defined (to either "" or something longer), the expansion returns "defined", and therefore test returns true. You could use any non-empty string in place of "defined", but readability is always nice. |
|
Line 49: | Line 66: |
# Bash 4.2 | # Bash 4.2 / ksh93 |
Line 53: | Line 70: |
Another way is to use a SubShell which will exit with an error code if an unbound variable is used: {{{ # bash/ksh work ... others? if (set -u; : $UNBOUND_VAR) 2>/dev/null ; then echo "the variable has been set" else echo "the variable has not been set" fi }}} |
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. |
Line 63: | Line 72: |
(This is much slower and uglier than simply using the `${var+defined}` expansion.) | === Testing that a function has been defined === |
Line 65: | Line 74: |
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: | 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. |
Line 68: | Line 77: |
# Bash # These two are best: if [[ $(declare -f foo) ]] # it prints nothing, if undefined if declare -f foo >/dev/null # it also sets the exit status |
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. |
Line 73: | Line 81: |
# These are a little more obvious, but... if [[ $(type foo 2>&1) = *' is a function'* ]] if type foo >/dev/null 2>&1 && ! type -f foo >/dev/null 2>&1 |
# 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 |
Line 77: | Line 84: |
A related question is, ''Why on earth does anyone ''want'' this? Why not just define the function already?'' I don't know. I think it has something to do with [[http://en.wikipedia.org/wiki/Reflection_%28computer_science%29|reflection]]. But people keep asking it, so.... |
How do I determine whether a variable is already defined? Or a function?
There are several ways to test for a defined or non-empty variable or function depending upon the requirements.
Contents
"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.
# POSIX ${var+:} false ! ${var+false} [ -n "${var+_}" ] [ "${var+_}" ]
Which of the above performs best varies from shell to shell. If POSIX isn't a requirement, the following [[ solution usually performs best.
# Bash/ksh [[ ${var+_} ]]
Another method that's occasionally seen is to check the status of typeset -p. The above method should be preferred over this.
# Bash/ksh/zsh typeset -p var >/dev/null 2>&1 # returns 0 if var exists, error otherwise
Bash 4.2 adds a -v test:
# Bash 4.2 / ksh93 if [[ -v var ]]; then echo "var is defined"; fi
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.
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.
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. # 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