Differences between revisions 4 and 6 (spanning 2 versions)
Revision 4 as of 2007-07-13 15:33:11
Size: 2260
Editor: GreyCat
Comment: gods, what a fucking mess.
Revision 6 as of 2007-07-28 04:53:56
Size: 2698
Editor: ppp020-014
Comment: add matching with extglob
Deletions are marked like this. Additions are marked like this.
Line 23: Line 23:
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 what you actually mean is "a valid floating-point number" or something else more complex, then you might prefer to use a regular expression. Here is a portable version, using {{{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
}}}

The leading test of {{{"$foo"}}} is to ensure that it is not an empty string. (An empty string would satisfy the regex, and changing the regex to avoid that is not worth the effort.)

Bash version 3 and above have regular expression support in the [[ command. However, due to serious bugs and syntax changes in Bash's [[ regex support, we '''do not recommend''' using it. Nevertheless, if I simply omit all Bash regex answers here, someone will come along and fill them in -- and they probably won't work, or won't contain all the caveats necessary. So, in the interest of preventing disasters, here are the Bash regex answers that you should not use.
Line 45: Line 57:
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}}}: Note that the parentheses in the {{{egrep}}} regular expression and the bash 3.2.patched regular expression don't require backslashes in front of them, whereas the ones in the bash 3.1 command do.
Line 47: Line 59:
Another possibility with bash is perhaps to use extglob:
Line 48: Line 61:
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
shopt -s extglob
[[ +1234.43 = *([+-])+([0-9])*(.+([0-9])) ]] && echo "this is a number"
Line 54: Line 64:

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

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. Here is a portable version, using 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

The leading test of "$foo" is to ensure that it is not an empty string. (An empty string would satisfy the regex, and changing the regex to avoid that is not worth the effort.)

Bash version 3 and above have regular expression support in the [[ command. However, due to serious bugs and syntax changes in Bash's [[ regex support, we do not recommend using it. Nevertheless, if I simply omit all Bash regex answers here, someone will come along and fill them in -- and they probably won't work, or won't contain all the caveats necessary. So, in the interest of preventing disasters, here are the Bash regex answers that you should not use.

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.

Note that the parentheses in the egrep regular expression and the bash 3.2.patched regular expression don't require backslashes in front of them, whereas the ones in the bash 3.1 command do.

Another possibility with bash is perhaps to use extglob:

shopt -s extglob
[[ +1234.43 = *([+-])+([0-9])*(.+([0-9])) ]] && echo "this is a number"

BashFAQ/054 (last edited 2022-08-01 10:02:57 by 89)