How do I test if a variable is a number in Bash?

2018-12-31 15:56发布

I just can't figure out how do I make sure an argument passed to my script is a number or not.

All I want to do is something like this:

test *isnumber* $1 && VAR=$1 || echo "need a number"

Any help?

标签: linux bash shell
30条回答
残风、尘缘若梦
2楼-- · 2018-12-31 16:35

I was looking at the answers and... realized that nobody thought about FLOAT numbers (with dot)!

Using grep is great too.
-E means extended regexp
-q means quiet (doesn't echo)
-qE is the combination of both.

To test directly in the command line:

$ echo "32" | grep -E ^\-?[0-9]?\.?[0-9]+$  
# answer is: 32

$ echo "3a2" | grep -E ^\-?[0-9]?\.?[0-9]+$  
# answer is empty (false)

$ echo ".5" | grep -E ^\-?[0-9]?\.?[0-9]+$  
# answer .5

$ echo "3.2" | grep -E ^\-?[0-9]?\.?[0-9]+$  
# answer is 3.2

Using in a bash script:

check=`echo "$1" | grep -E ^\-?[0-9]*\.?[0-9]+$`

if [ "$check" != '' ]; then    
  # it IS numeric
  echo "Yeap!"
else
  # it is NOT numeric.
  echo "nooop"
fi

To match JUST integers, use this:

# change check line to:
check=`echo "$1" | grep -E ^\-?[0-9]+$`
查看更多
呛了眼睛熬了心
3楼-- · 2018-12-31 16:35

Can't comment yet so I'll add my own answer, which is an extension to glenn jackman's answer using bash pattern matching.

My original need was to identify numbers and distinguish integers and floats. The function definitions deducted to:

function isInteger() {
    [[ ${1} == ?(-)+([0-9]) ]]
}

function isFloat() {
    [[ ${1} == ?(-)@(+([0-9]).*([0-9])|*([0-9]).+([0-9]))?(E?(-|+)+([0-9])) ]]
}

I used unit testing (with shUnit2) to validate my patterns worked as intended:

oneTimeSetUp() {
    int_values="0 123 -0 -123"
    float_values="0.0 0. .0 -0.0 -0. -.0 \
        123.456 123. .456 -123.456 -123. -.456
        123.456E08 123.E08 .456E08 -123.456E08 -123.E08 -.456E08 \
        123.456E+08 123.E+08 .456E+08 -123.456E+08 -123.E+08 -.456E+08 \
        123.456E-08 123.E-08 .456E-08 -123.456E-08 -123.E-08 -.456E-08"
}

testIsIntegerIsFloat() {
    local value
    for value in ${int_values}
    do
        assertTrue "${value} should be tested as integer" "isInteger ${value}"
        assertFalse "${value} should not be tested as float" "isFloat ${value}"
    done

    for value in ${float_values}
    do
        assertTrue "${value} should be tested as float" "isFloat ${value}"
        assertFalse "${value} should not be tested as integer" "isInteger ${value}"
    done

}

Notes: The isFloat pattern can be modified to be more tolerant about decimal point (@(.,)) and the E symbol (@(Ee)). My unit tests test only values that are either integer or float, but not any invalid input.

查看更多
初与友歌
4楼-- · 2018-12-31 16:35
test -z "${i//[0-9]}" && echo digits || echo no no no

${i//[0-9]} replaces any digit in the value of $i with an empty string, see man -P 'less +/parameter\/' bash. -z checks if resulting string has zero length.

if you also want to exclude the case when $i is empty, you could use one of these constructions:

test -n "$i" && test -z "${i//[0-9]}" && echo digits || echo not a number
[[ -n "$i" && -z "${i//[0-9]}" ]] && echo digits || echo not a number
查看更多
梦醉为红颜
5楼-- · 2018-12-31 16:35

I tried ultrasawblade's recipe as it seemed the most practical to me, and couldn't make it work. In the end i devised another way though, based as others in parameter substitution, this time with regex replacement:

[[ "${var//*([[:digit:]])}" ]]; && echo "$var is not numeric" || echo "$var is numeric"

It removes every :digit: class character in $var and checks if we are left with an empty string, meaning that the original was only numbers.

What i like about this one is its small footprint and flexibility. In this form it only works for non-delimited, base 10 integers, though surely you can use pattern matching to suit it to other needs.

查看更多
看风景的人
6楼-- · 2018-12-31 16:35
printf '%b' "-123\nABC" | tr '[:space:]' '_' | grep -q '^-\?[[:digit:]]\+$' && echo "Integer." || echo "NOT integer."

Remove the -\? in grep matching pattern if you don't accept negative integer.

查看更多
ら面具成の殇う
7楼-- · 2018-12-31 16:41

http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_04_03.html

You can also use bash's character classes.

if [[ $VAR = *[[:digit:]]* ]]; then
 echo "$VAR is numeric"
else
 echo "$VAR is not numeric"
fi

Numerics will include space, the decimal point, and "e" or "E" for floating point.

But, if you specify a C-style hex number, i.e. "0xffff" or "0XFFFF", [[:digit:]] returns true. A bit of a trap here, bash allows you do to something like "0xAZ00" and still count it as a digit (isn't this from some weird quirk of GCC compilers that let you use 0x notation for bases other than 16???)

You might want to test for "0x" or "0X" before testing if it's a numeric if your input is completely untrusted, unless you want to accept hex numbers. That would be accomplished by:

if [[ ${VARIABLE:1:2} = "0x" ]] || [[ ${VARIABLE:1:2} = "0X" ]]; then echo "$VAR is not numeric"; fi
查看更多
登录 后发表回答