Checking if an input number is an integer
I’m trying to check if an input is an integer and I’ve gone over it a hundred times but don’t see the error in this. Alas it does not work, it triggers the if statement for all inputs (numbers/letters)
read scale
if ! [[ "$scale" =~ "^[0-9]+$" ]]
then
echo "Sorry integers only"
fi
I’ve played around with the quotes but either missed it or it did nothing. What do I do wrong? Is there an easier way to test if an input is just an INTEGER?
Remove quotes
if ! [[ "$scale" =~ ^[0-9]+$ ]]
then
echo "Sorry integers only"
fi
( scale=${scale##*[!0-9]*}
: ${scale:?input must be an integer}
) || exit
That does the check and outputs your error.
For unsigned integers I use:
read -r scale
[ -z "${scale//[0-9]}" ] && [ -n "$scale" ] || echo "Sorry integers only"
Tests:
$ ./test.sh
7
$ ./test.sh
777
$ ./test.sh
a
Sorry integers only
$ ./test.sh
""
Sorry integers only
$ ./test.sh
Sorry integers only
As the OP seems to want only positive integers:
[ "$1" -ge 0 ] 2>/dev/null
Examples:
$ is_positive_int(){ [ "$1" -ge 0 ] 2>/dev/null && echo YES || echo no; }
$ is_positive_int word
no
$ is_positive_int 2.1
no
$ is_positive_int -3
no
$ is_positive_int 42
YES
Note that a single [
test is required:
$ [[ "word" -eq 0 ]] && echo word equals zero || echo nope
word equals zero
$ [ "word" -eq 0 ] && echo word equals zero || echo nope
-bash: [: word: integer expression expected
nope
This is because dereferencing occurs with [[
:
$ word=other
$ other=3
$ [[ $word -eq 3 ]] && echo word equals other equals 3
word equals other equals 3
A POSIX and portable solution is:
read scale
if [ -z "${scale##*[!0-9]*}" ];
then echo "Sorry integers only"
fi
In bash, you can use the extended patterns inside [[...]]
:
scale=4
[[ $scale == +([[:digit:]]) ]] || echo "Sorry integers only"
# ==> no output
scale=4.0
[[ $scale == +([[:digit:]]) ]] || echo "Sorry integers only"
# ==> Sorry integers only
For X greater than 0 I use
[ 0$X -gt 0 2>/dev/null ] && echo "is number" || echo "is string"
this gives no error if X is empty
POSIXly:
case $1 in
("" | *[!0123456789]*)
echo >&2 Sorry, decimal integer only
exit 1
esac
Do not use [0-9]
which often matches a lot more than [0123456789]
(especially with bash
globs, YMMV for bash’s [[ =~ ]]
operator which uses the system regexps where [0-9]
may or may not match more than 0123456789
depending on the system and locale).
More on that at what is the meaning of this shell script function
Note that you may also want to reject numbers like 08
or 09
which some tools (including bash arithmetic operators) reject as invalid octal numbers.
Less convoluted
The negation is now in the regular expression instead of the test condition.
if [[ $scale =~ [^0-9] ]]
then
echo "Sorry integers only"
fi
Explanation:
=~
binary operator where the string to the right of the operator is considered an extended regular expression and matched accordingly (as in regex(3))[^0-9]
matches a single character not present in the list 0-9.
The following is POSIX compliant, so it will work in other shells than bash as well. Just checking -z "${scale##*[!0-9]*}"
as suggested by another answer here does not take into account that strings starting with zero (except for zero itself) or empty strings are also not valid base-10 integers.
case "$scale" in *[!0123456789]*|0?*|"")
echo "Sorry integers only";;
esac
We need to spell out all valid digits because using a range like 0-9
matches on any character (or possibly multi-character collation element) that sorts in between 0 and 9. Thanks to @stéphane-chazelas for this insight. More about that here: https://lists.gnu.org/archive/html/bug-bash/2021-02/msg00054.html
If negative integers are valid in your use case, you can use:
case "$scale" in *[!0123456789-]*|-|?*-*|0?*|-0|"")
echo "Sorry integers only";;
esac