How do I check to see if a variable is a number, or contains a number, in UNIX shell?
-
1The title asks if a variable is a number, the description asks if it contains a number. Which do you want? Also, when you say number d o you mean integer or should it handle decimals? – gpojd Nov 21 '08 at 19:35
-
Do you consider 3.1415 a number? – Jens May 15 '14 at 09:28
14 Answers
if echo $var | egrep -q '^[0-9]+$'; then
# $var is a number
else
# $var is not a number
fi

- 390,455
- 97
- 512
- 589
-
I don't know that this is correct, this checks that it is an integer (not a number). I would change the regexp to '[0-9]' to satisfy the problem if the question is in the description ("contains"), and change it to handle non-integer numbers if it is the question in the title ("is"). – gpojd Nov 21 '08 at 19:33
-
How many thousand of cycles does the fork and pipe and egrep waste for such a simple operation that could be done in the shell? Hope this isn't needed in a tight loop... – Jens May 15 '14 at 09:31
-
@Jens: You're right, this answer is not as efficient as one that doesn't spawn a subprocess, though most of the time that won't make any practical difference. I've up-voted your better answer. – Adam Rosenfield May 15 '14 at 17:32
-
@AdamRosenfield Thanks, that's a noble gesture in the face of criticism. The world needs more of that attitude. – Jens May 19 '14 at 08:27
-
Don't forget negative numbers. Assuming we're only talking about integers (whole numbers), not floating point (real) numbers - i.e., numbers that work with test(1)'s numeric operators (-eq, etc.), we could tweak the regex method here to include negative whole numbers like so: `echo $var | egrep -q '^-*[[:digit:]]+$' && echo is num || echo not num` (only for base 10, of course). That even works for excessively negated but legit numbers like ----234. – Juan May 02 '15 at 15:39
Shell variables have no type, so the simplest way is to use the return type test
command:
if [ $var -eq $var 2> /dev/null ]; then ...
(Or else parse it with a regexp)

- 9,442
- 2
- 28
- 26
-
2If var is empty, this gives a false positive. Maybe: `if [ -n "$var" -a $var -eq $var 2> /dev/null ]; then echo is num; else echo not a num; fi` – Juan May 02 '15 at 15:27
-
Also nice that this works for basic bourne/korn shells (bashisms not needed). One downside: in the test(1) I used (FreeBSD 9, Fedora 20), this fails when var is excessively negated (e.g., --234), although the arithmetic operations work (e.g., `echo $((--234 * 2))`). – Juan May 02 '15 at 15:48
-
No forks, no pipes. Pure POSIX shell:
case $var in
(*[!0-9]*|'') echo not a number;;
(*) echo a number;;
esac
(Assumes number := a string of digits). If you want to allow signed numbers with a single leading -
or +
as well, strip the optional sign like this:
case ${var#[-+]} in
(*[!0-9]*|'') echo not a number;;
(*) echo a number;;
esac

- 69,818
- 15
- 125
- 179
-
-
-
Fair enough. I just thought I'd make it clear on all the various solutions. On some others, it's fairly easy to support negative numbers. Trying to support negative numbers using this method (normal globbing) is tougher. – Juan May 03 '15 at 22:40
-
@Juan I've added an easy modification to allow signed numbers. Thanks for the challenge :-) – Jens Apr 22 '17 at 09:31
-
Nice addition. Still doesn't work for perverse but valid --123. Interesting that freebsd's /bin/sh < 10 does (`sh -c 'var=--123; echo $(($var))'`) handle that. But freebsd >= 10 does not. – Juan Apr 28 '17 at 20:36
-
Turns out the --123 not working in freebsd 10+ was intentional - to explicitly indicate that the -- operator is not supported. '- -123' and '-(-123)' work (but are still problematic for the above number detection code). – Juan May 01 '17 at 01:21
In either ksh93 or bash with the extglob option enabled:
if [[ $var == +([0-9]) ]]; then ...
-
To handle negative numbers (even excessively but legitimately negated like ---234), tweak that to `if [[ $var == +(-*[[:digit:]]) ]]; then echo is num; else echo not num; fi`. Nice that this handles empty or unset `var` as well as multi-word `var` (e.g., 'brown cow'). Use `[:xdigit:]` character class for base 16 nums. Note: locale independent character classes (e.g., [:digit:]) don't seem to work with ksh. – Juan May 02 '15 at 16:30
-
This gets complicated quickly. Try testing with 'brown cow', '123Q', '0xAbc', '123 Q', '-123', '-----345'. And the varied implementations of 'extended regular expressions' in bash/ksh contribute to the complexity. Downvote for lack of portability for the basic posix shells (more a downvote for the method rather than Darron's answer since he was very up front with that limitation). – Juan May 02 '15 at 17:33
-
can u guys help me to understand this: # x=200 # [[ +([0-9]) == $x ]] && echo number || echo not number > not a number # [[ $x == +([0-9]) ]] && echo number || echo not number > number – Thiago Conrado Jul 03 '20 at 18:43
-
@ThiagoConrado This is the same problem as expecting a=5 does the same as 5=a. The `==` operator is not symmetric in what it accepts on each side. – Jens Feb 28 '22 at 16:12
Here's a version using only the features available in a bare-bones shell (ie it'd work in sh
), and with one less process than using grep
:
if expr "$var" : '[0-9][0-9]*$'>/dev/null; then
echo yes
else
echo no
fi
This checks that the $var
represents only an integer; adjust the regexp to taste, and note that the expr
regexp argument is implicitly anchored at the beginning.

- 11,978
- 2
- 33
- 56
-
-
For negated numbers: `expr "$var" : '-*[0-9][0-9]*$' && echo num || echo not num` – Juan May 02 '15 at 16:43
-
Upvote for portability (e.g., no bashisms); slight downside for extra fork. – Juan May 02 '15 at 16:45
-
-
the regex should probably be `'-\?[1-9][0-9]*$'` or `'-\?[0-9]\+$`' depending on if you want to allow leading zeros or not and negative numbers or not – ljden May 25 '22 at 01:39
This can be checked using regular expression.
###
echo $var|egrep '^[0-9]+$'
if [ $? -eq 0 ]; then
echo "$var is a number"
else
echo "$var is not a number"
fi
I'm kind of newbee on shell programming so I try to find out most easy and readable It will just check the var is greater or same as 0 I think it's nice way to choose parameters... may be not what ever... :
if [ $var -ge 0 2>/dev/null ] ; then ...

- 21
- 2
-
if [ $var -ge 0 -o $var -lt 0 ]; then ... # to handle negative numbers – John Eikenberry Nov 26 '15 at 21:46
INTEGER
if echo "$var" | egrep -q '^\-?[0-9]+$'; then
echo "$var is an integer"
else
echo "$var is not an integer"
fi
tests (with var=2 etc.):
2 is an integer
-2 is an integer
2.5 is not an integer
2b is not an integer
NUMBER
if echo "$var" | egrep -q '^\-?[0-9]*\.?[0-9]+$'; then
echo "$var is a number"
else
echo "$var is not a number"
fi
tests (with var=2 etc.):
2 is a number
-2 is a number
-2.6 is a number
-2.c6 is not a number
2. is not a number
2.0 is a number

- 19
- 4
-
Sure, .6 is a number. The command also shows it as a number. Also 4. is a number but this command tells its not a number. – Oguz Oct 06 '15 at 07:22
if echo $var | egrep -q '^[0-9]+$'
Actually this does not work if var is multiline.
ie
var="123
qwer"
Especially if var comes from a file :
var=`cat var.txt`
This is the simplest :
if [ "$var" -eq "$var" ] 2> /dev/null
then echo yes
else echo no
fi

- 1,678
- 17
- 10
-
Slight downvote for repetition of Piotr's 2008 answer without attribution. Same false positive problem as with that one (add `-n "$var"` to catch empty/unset var). See comments there. Good point on potential false positives for the multiline issue. – Juan May 02 '15 at 17:20
Here is the test without any regular expressions (tcsh code):
Create a file checknumber:
#! /usr/bin/env tcshif ( "$*" == "0" ) then
exit 0 # number
else
((echo "$*" | bc) > /tmp/tmp.txt) >& /dev/null
set tmp = `cat /tmp/tmp.txt`
rm -f /tmp/tmp/txt
if ( "$tmp" == "" || $tmp == 0 ) then
exit 1 # not a number
else
exit 0 # number
endif
endif
and run
chmod +x checknumber
Use
checknumber -3.45
and you'll got the result as errorlevel ($?).
You can optimise it easily.
-
Fork heavy. Lighter implementations exist for whole numbers (see previous answers) unless all you have is csh. Slight upvote for handling floating point, although I am guessing that's not what the OP wanted (the OP was quite vague on the problem description). – Juan May 02 '15 at 17:27
( test ! -z "$num" && test "$num" -eq "$num" 2> /dev/null ) && {
# $num is a number
}

- 152
- 1
- 5
-
This will fail in unattractive ways because of the [lack of quoting](https://stackoverflow.com/questions/10067266/when-to-wrap-quotes-around-a-shell-variable) for any nontrivial string input. – tripleee Mar 21 '21 at 16:34
-
Is the `-z` check really necessary though? `test "" -eq "" 2>/dev/null` does what I expect here (old Bash 3.2 on standard macOS). Regardless, the parentheses seem superfluous (and cost you an unnecessary subshell). – tripleee Mar 25 '21 at 11:10
a=123
if [ `echo $a | tr -d [:digit:] | wc -w` -eq 0 ]
then
echo numeric
else
echo ng
fi
numeric
a=12s3
if [ `echo $a | tr -d [:digit:] | wc -w` -eq 0 ]
then
echo numeric
else
echo ng
fi
ng
-
2
-
@StevenWestbrook: this removes digits from $a and counts what's left over. If there is anything left, it's not a string of only digits ("not numeric"). – Juan May 02 '15 at 16:48
-
Doesn't handle negative numbers (e.g., -123, --123). Unfortunately, adding support for neg numbers using this fork-heavy method means adding another `tr -d -` in the pipeline I think. – Juan May 02 '15 at 16:49
-
You can alter the tr(1) expression to '[[:digit:]-]' to catch negative numbers (or use [:xdigit:] for base 16 numbers), but that gives a false positive with something like 123-54 (thinks that's a valid number). – Juan May 02 '15 at 17:15
You can do that with simple test command.
$ test ab -eq 1 >/dev/null 2>&1
$ echo $?
2
$ test 21 -eq 1 >/dev/null 2>&1
$ echo $?
1
$ test 1 -eq 1 >/dev/null 2>&1
$ echo $?
0
So if the exit status is either 0 or 1 then it is a integer , but if the exis status is 2 then it is not a number.

- 1,743
- 3
- 21
- 27
-
Downvote - essentially the same as @Piotr's 2008 answer. Doesn't anyone review existing answers before replying - especially in the same thread (let alone other similar threads)? – Juan May 02 '15 at 17:22
Taking the value from Command line and showing THE INPUT IS DECIMAL/NON-DECIMAL and NUMBER or not:
NUMBER=$1
IsDecimal=`echo "$NUMBER" | grep "\."`
if [ -n "$IsDecimal" ]
then
echo "$NUMBER is Decimal"
var1=`echo "$NUMBER" | cut -d"." -f1`
var2=`echo "$NUMBER" | cut -d"." -f2`
Digit1=`echo "$var1" | egrep '^-[0-9]+$'`
Digit2=`echo "$var1" | egrep '^[0-9]+$'`
Digit3=`echo "$var2" | egrep '^[0-9]+$'`
if [ -n "$Digit1" ] && [ -n "$Digit3" ]
then
echo "$NUMBER is a number"
elif [ -n "$Digit2" ] && [ -n "$Digit3" ]
then
echo "$NUMBER is a number"
else
echo "$NUMBER is not a number"
fi
else
echo "$NUMBER is not Decimal"
Digit1=`echo "$NUMBER" | egrep '^-[0-9]+$'`
Digit2=`echo "$NUMBER" | egrep '^[0-9]+$'`
if [ -n "$Digit1" ] || [ -n "$Digit2" ]; then
echo "$NUMBER is a number"
else
echo "$NUMBER is not a number"
fi
fi