Differences between revisions 37 and 38
 ⇤ ← Revision 37 as of 2014-05-04 22:01:39 → Size: 3453 Editor: ormaaj Comment: Adding the case of a bare +/- or empty doesn't complicate things much (I think). Needs testing. ← Revision 38 as of 2014-05-04 22:03:53 → ⇥ Size: 3515 Editor: ormaaj Comment: oops Deletions are marked like this. Additions are marked like this. Line 43: Line 43: if [[ \$foo = @(*[0-9]*|[+-]|) && \$foo = ?([+-])*([0-9])?(.*([0-9])) ]]; then if [[ \$foo = @(*[0-9]*|!([+-]|)) && \$foo = ?([+-])*([0-9])?(.*([0-9])) ]]; then Line 48: Line 48: Optionally, `case..esac` may have been used in shells with extended pattern matching. The leading test of {{{\$foo}}} is to ensure that it contains at least one digit. Optionally, `case..esac` may have been used in shells with extended pattern matching. The leading test of {{{\$foo}}} is to ensure that it contains at least one digit, isn't empty, and contains more than just + or - by itself.

## 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 when people ask this seems to be "a non-negative integer, with no leading + sign". Or in other words, a string of all digits. Other times, people want to validate a floating-point input, with optional sign and optional decimal point.

### Hand parsing

If you're validating a simple "string of digits", you can do it with a glob:

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

The same thing can be done in Korn and POSIX shells as well, using case:

```# POSIX
case \$var in
*[!0-9]*|'')
printf '%s has a non-digit somewhere in it\' "\$var"
;;
*)
printf '%s is strictly numeric\n' "\$var"
esac >&2```

If you need to allow a leading negative sign, or if want a valid floating-point number or something else more complex, then there are a few possible ways. Standard globs aren't expressive enough to do this, but we can use extended globs:

```# Bash -- extended globs must be enabled explicitly in versions prior to 4.1.
# Check whether the variable is all digits.
shopt -s extglob
[[ \$var == +([0-9]) ]]```

A more complex case:

```# Bash / ksh
shopt -s extglob

if [[ \$foo = @(*[0-9]*|!([+-]|)) && \$foo = ?([+-])*([0-9])?(.*([0-9])) ]]; then
echo 'foo is a floating-point number'
fi```

Optionally, case..esac may have been used in shells with extended pattern matching. The leading test of \$foo is to ensure that it contains at least one digit, isn't empty, and contains more than just + or - by itself.

If your definition of "a valid number" is even more complex, or if you need a solution that works in legacy Bourne shells, you might prefer to use an external tool's regular expression syntax. Here is a portable version (explained in detail here), using egrep:

```# Bourne
if echo "\$foo" | grep -qE '^[-+]?([0-9]+\.?|[0-9]*\.[0-9]+)\$'; then
echo "'\$foo' is a number"
else
echo "'\$foo' is not a number"
fi```

Bash version 3 and above have regular expression support in the [[ command.

```# Bash
# The regexp must be stored in a var and expanded for backward compatibility with versions < 3.2

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

### Using the parsing done by [ and printf (or "using eq")

```# fails with ksh
if [ "\$foo" -eq "\$foo" ] 2>/dev/null; then
echo "\$foo is an integer"
fi```

[ parses the variable and interprets it as an integer because of the -eq. If the parsing succeeds the test is trivially true; if it fails [ prints an error message that 2>/dev/null hides and sets a status different from 0. However this method fails if the shell is ksh, because ksh evaluates the variable as an arithmetic expression.

You can use a similar trick with printf, but this won't work in all shells either:

```# BASH
if printf %f "\$foo" >/dev/null 2>&1; then
echo "\$foo is a float"
fi```

You can use %d to parse an integer. Take care that the parsing might be (is supposed to be?) locale-dependent.

BashFAQ/054 (last edited 2022-04-19 05:23:33 by emanuele6)