Differences between revisions 7 and 25 (spanning 18 versions)
Revision 7 as of 2008-06-12 20:58:49
Size: 2101
Editor: GreyCat
Comment: clean up the set -u example
Revision 25 as of 2016-07-24 18:05:27
Size: 4541
Editor: atlantic850
Comment:
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
[[Anchor(faq83)]] <<Anchor(faq83)>>
Line 3: Line 3:
There are several ways to test for a defined or non-empty variable or function depending upon the requirements.
Line 4: Line 5:
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: <<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 and not so ancient pdksh or ash versions. 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 ]]` || Also works in Bash, Ksh and Zsh since version 5.0.6. Same as above, only more ambiguous. ||
|| `(( ${#var} ))` || About as portable as the above though hits some bugs in some versions of Ksh when the variable contains invalid characters. Almost always slower. Don't use this to test for empty strings. ||
|| `case "$var" in "")` || Extremely portable. You'll probably want to use that if you want to check for different values including the empty string. ||

To check the converse, whether a variable is empty (unset or zero-length), invert the above logic.
Line 7: Line 28:
if test -n "$var"
if [ -n "$var" ]
if test "$var"
if [ "$var" ]
if [[ -n $var ]]
if [[ $var ]]
test x"$var" = x
test -z "$var"
[ -z "$var" ]
test ! "$var"
[ ! "$var" ]
[[ -z $var ]]
[[ ! $var ]] # Also won't work in Zsh prior to 5.0.6. If such portability is needed, adjust solutions on this page to use -z or -n explicitly.
case "$var" in "") ;; *)...
Line 15: Line 38:
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, but there are some [:BashFAQ/073:parameter expansion] tricks that can be used. Here is the simplest: === Testing that a variable has been declared ===

D
istinguishing between a variable that is ''undefined'' and one that is ''defined but empty'' is somewhat trickier, but possible.
Line 18: Line 43:
# Bourne
if test "${foo+defined}"
# Bourne / POSIX:
${var+':'} false
case ${var+y} in y)...
[ -n "${var+_}" ]
Line 22: Line 49:
This expansion results in nothing if foo is undefined. Therefore test returns false.
If foo 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.
Which of the above performs best varies from shell to shell. If POSIX isn't a requirement, the following `[[` solution usually performs best.
Line 26: Line 51:
Another way is to use a SubShell which will exit with an error code if an unbound variable is used:
Line 28: Line 52:
# 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
# Bash/ksh
[[ ${var+_} ]]
(( ${var+1} ))
Line 36: Line 57:
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: Another method that's occasionally seen is to check the status of `typeset -p`. The above method should be preferred over this.
Line 39: Line 60:
# 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

# 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/zsh
typeset -p var >/dev/null 2>&1
# returns 0 if var exists, error otherwise
Line 49: Line 65:
A related question is, ''Why on earth does anyone ''want'' this? Why not just define the function already?'' Bash 4.2 adds a `-v` test:
Line 51: Line 67:
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.... {{{
# Bash 4.2 / ksh93
if [[ -v var ]]; then echo "var is defined"; fi
}}}

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.

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

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.

"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 and not so ancient pdksh or ash versions. 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 ]]

Also works in Bash, Ksh and Zsh since version 5.0.6. Same as above, only more ambiguous.

(( ${#var} ))

About as portable as the above though hits some bugs in some versions of Ksh when the variable contains invalid characters. Almost always slower. Don't use this to test for empty strings.

case "$var" in "")

Extremely portable. You'll probably want to use that if you want to check for different values including the empty string.

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 prior to 5.0.6. If such portability is needed, adjust solutions on this page to use -z or -n explicitly.
case "$var" in "") ;; *)...

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.

# Bourne / POSIX:
${var+':'} false
case ${var+y} in y)...
[ -n "${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+_} ]]
(( ${var+1} ))

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

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)