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?

Asked By: lonewarrior556

||

Use -eq operator of test command:

read scale
if ! [ "$scale" -eq "$scale" ] 2> /dev/null
then
    echo "Sorry integers only"
fi

It not only works in bash but also any POSIX shell. From POSIX test documentation:

n1 -eq  n2
    True if the integers n1 and n2 are algebraically equal; otherwise, false.
Answered By: cuonglm

Remove quotes

if ! [[ "$scale" =~ ^[0-9]+$ ]]
    then
        echo "Sorry integers only"
fi
Answered By: jimmij
( scale=${scale##*[!0-9]*}
: ${scale:?input must be an integer}
) || exit

That does the check and outputs your error.

Answered By: mikeserv

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
Answered By: raciasolvo

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
Answered By: Tom Hale

A POSIX and portable solution is:

read scale
if     [ -z "${scale##*[!0-9]*}" ]; 
then   echo "Sorry integers only"
fi
Answered By: user232326

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
Answered By: glenn jackman

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

Answered By: illulix

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.

Answered By: Stéphane Chazelas

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.
Answered By: Serge Stroobandt

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
Answered By: josch