How do I test if a variable is a number in Bash?
One approach is to use a regular expression, like so:
re='^[0-9]+$'
if ! [[ $yournumber =~ $re ]] ; then
echo "error: Not a number" >&2; exit 1
fi
If the value is not necessarily an integer, consider amending the regex appropriately; for instance:
^[0-9]+([.][0-9]+)?$
...or, to handle numbers with a sign:
^[+-]?[0-9]+([.][0-9]+)?$
Nobody suggested bash's extended pattern matching:
[[ $1 == ?(-)+([0-9]) ]] && echo "$1 is an integer"
or using a POSIX character class:
[[ $1 == ?(-)+([[:digit:]]) ]] && echo "$1 is an integer"
The following solution can also be used in basic shells such as Bourne without the need for regular expressions. Basically any numeric value evaluation operations using non-numbers will result in an error which will be implicitly considered as false in shell:
"$var" -eq "$var"
as in:
#!/bin/bash
var=a
if [ -n "$var" ] && [ "$var" -eq "$var" ] 2>/dev/null; then
echo number
else
echo not a number
fi
You can can also test for $? the return code of the operation which is more explicit:
[ -n "$var" ] && [ "$var" -eq "$var" ] 2>/dev/null
if [ $? -ne 0 ]; then
echo $var is not number
fi
Redirection of standard error is there to hide the "integer expression expected" message that bash prints out in case we do not have a number.
CAVEATS (thanks to the comments below):
- Numbers with decimal points are not identified as valid "numbers"
- Using
[[ ]]
instead of[ ]
will always evaluate totrue
- Most non-Bash shells will always evaluate this expression as
true
- The behavior in Bash is undocumented and may therefore change without warning
- If the value includes spaces after the number (e.g. "1 a") produces error, like
bash: [[: 1 a: syntax error in expression (error token is "a")
- If the value is the same as var-name (e.g. i="i"), produces error, like
bash: [[: i: expression recursion level exceeded (error token is "i")
Without bashisms (works even in the System V sh),
case $string in
''|*[!0-9]*) echo bad ;;
*) echo good ;;
esac
This rejects empty strings and strings containing non-digits, accepting everything else.
Negative or floating-point numbers need some additional work. An idea is to exclude -
/ .
in the first "bad" pattern and add more "bad" patterns containing the inappropriate uses of them (?*-*
/ *.*.*
)