Differences between revisions 11 and 28 (spanning 17 versions)
Revision 11 as of 2011-02-17 16:32:44
Size: 2468
Editor: GreyCat
Comment: test -v (bash 4.2), etc.
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 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 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 6: Line 6:
if test -n "$var"
if [ -n "$var" ]
if test "$var"
if [ "$var" ]
if [[ -n $var ]]
if [[ $var ]]
}}}

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":

{{{
# Bash
declare -p var >/dev/null 2>&1
# returns 0 if var exists, error otherwise
}}}

Here's another one that uses [[BashFAQ/073|parameter expansion]]:

{{{
# Bourne
if test "${var+defined}"
}}}

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.

Bash 4.2 adds a `-v` test:

{{{
# Bash 4.2
if [[ -v var ]]; then echo "var is defined"; fi
}}}

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"
# POSIX
if test "$var"; then
  echo "The variable has a non-empty value."
Line 50: Line 12:
(This is much slower and uglier than simply using the `${var+defined}` expansion.) If this fails for you because you use `set -u`, please see [[BashFAQ/112|FAQ 112]].
Line 52: Line 14:
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: If we wish to distinguish between an ''empty'' variable and an ''unset'' variable, then we may use the `+` [[BashFAQ/073|parameter expansion]]:
Line 55: Line 17:
# 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
# POSIX
if test "${var+defined}"; then
  echo "The variable is defined."
fi
Line 65: Line 23:
A related question is, ''Why on earth does anyone ''want'' this? Why not just define the function already?'' 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.
Line 67: Line 25:
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.... 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 [[BashFAQ/073|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
}}}

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)