Anchor(faq54)

How can I tell whether a variable contains a valid number?

First, you have to define what you mean by "number". The most common case seems to be that, when people ask this, they actually mean "a non-negative integer, with no leading + sign".

if [[ $foo = *[^0-9]* ]]; then
   echo "'$foo' has a non-digit somewhere in it"
else
   echo "'$foo' is strictly numeric"
fi

This can be done in Korn and legacy Bourne shells as well, using case:

case "$foo" in
    *[!0-9]*) echo "'$foo' has a non-digit somewhere in it" ;;
    *) echo "'$foo' is strictly numeric" ;;
esac

If what you actually mean is "a valid floating-point number" or something else more complex, then you might prefer to use a regular expression. Bash version 3 and above have regular expression support in the [[ command:

if [[ $foo && $foo =~ ^[-+]?[0-9]*\(\.[0-9]+\)?$ ]]; then  # Bash 3.1 only!
    echo "'$foo' looks rather like a number"
else
    echo "'$foo' doesn't look particularly numeric to me"
fi

Unfortunately, bash changed the syntax of its regular expression support after version 3.1, so the following may work in some patched versions of bash 3.2:

if [[ $foo && $foo =~ ^[-+]?[0-9]*(\.[0-9]+)?$ ]]; then    # **PATCHED** Bash 3.2 only!
    echo "'$foo' looks rather like a number"
else
    echo "'$foo' doesn't look particularly numeric to me"
fi

It fails rather spectacularly in bash 3.1 and in bash 3.2 without patches.

If you don't have bash version 3, or if you simply don't want to bother screwing around with broken shell features, then you would use egrep:

if test "$foo" && echo "$foo" | egrep '^[-+]?[0-9]*(\.[0-9]+)?$' >/dev/null; then
    echo "'$foo' might be a number"
else
    echo "'$foo' might not be a number"
fi

Note that the parentheses in the egrep regular expression don't require backslashes in front of them, whereas the ones in the bash3 command (the 3.1 version, not the 3.2 version!) do. Also, the leading test of "$foo" (in both versions) is to ensure that it is not an empty string. (An em,pty string would satisfy the regex, and changing the regex to avoid that is not worth the effort.)