如何testingvariables是否是Bash中的数字?

我只是不知道如何确保传递给我的脚本的参数是否是一个数字。

我想要做的就是这样的事情:

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

任何帮助?

一种方法是使用正则expression式,如下所示:

 re='^[0-9]+$' if ! [[ $yournumber =~ $re ]] ; then echo "error: Not a number" >&2; exit 1 fi 

如果该值不一定是整数,则考虑正确修改正则expression式; 例如:

 ^[0-9]+([.][0-9]+)?$ 

…或处理负数:

 ^-?[0-9]+([.][0-9]+)?$ 

没有bashisms(即使在System V中也是有效的),

 case $string in ''|*[!0-9]*) echo bad ;; *) echo good ;; esac 

这将拒绝空string和包含非数字的string,接受其他所有内容。

负数或浮点数需要一些额外的工作。 一个想法是排除- / . 在第一个“坏”的模式,并添加更多的“坏”模式,包含他们的不当使用( ?*-* / *.*.*

我使用这个:

 "$var" -eq "$var" 

如下所示:

 #!/bin/bash var=a if [ "$var" -eq "$var" ] 2>/dev/null; then echo number else echo not a number fi 

标准错误的redirect是为了隐藏在没有数字的情况下打印出来的“期望的整数expression式”消息。

CAVEATS (感谢下面的评论):

  • 带小数点的数字被识别为有效的“数字”
  • 使用[[ ]]而不是[ ]将始终评估为true
  • 大多数非Bash shell总是将这个expression式评估为true
  • Bash中的行为没有logging,因此可能会在没有警告的情况下更改
  • 如果值包含数字后面的空格(如“1”)会产生错误,如bash: [[: 1 a: syntax error in expression (error token is "a")
  • 如果value与var-name(例如i =“i”)相同,会产生错误,如bash: [[: i: expression recursion level exceeded (error token is "i")

我很惊讶直接parsingshell中的数字格式的解决scheme。 shell不太适合这个,是一个控制文件和进程的DSL。 有足够多的数字parsing器,例如:

 isdecimal() { # filter octal/hex/ord() num=$(printf '%s' "$1" | sed "s/^0*\([1-9]\)/\1/; s/'/^/") test "$num" && printf '%f' "$num" >/dev/null 2>&1 } 

将“%f”更改为您所需的任何特定格式。

这testing如果一个数字是一个非负整数,并且是独立于shell(即没有bashisms),并且只使用shell内置函数:

 [ -z "${num##[0-9]*}" ] && echo "is a number" || echo "is not a number"; 

但是错误
正如jilles在他的回答中评论和build议的那样,这是使用shell模式的正确方法。

 [ ! -z "${num##*[!0-9]*}" ] && echo "is a number" || echo "is not a number"; 

没有人build议bash的扩展模式匹配 :

 [[ $1 == ?(-)+([0-9]) ]] && echo "$1 is an integer" 

只需跟进@mary。 但是因为我没有足够的代表,所以不能发表这个post作为评论。 无论如何,这是我用的:

 isnum() { awk -va="$1" 'BEGIN {print (a == a + 0)}'; } 

如果参数是一个数字,函数将返回“1”,否则返回“0”。 这适用于整数以及浮动。 用法是这样的:

 n=-2.05e+07 res=`isnum "$n"` if [ "$res" == "1" ]; then echo "$n is a number" else echo "$n is not a number" fi 

我正在看答案,并意识到没有人想到FLOAT数字(带点)!

使用grep也很好。
-E表示扩展的正则expression式
-q表示安静(不回显)
-qE是两者的组合。

直接在命令行中进行testing:

 $ 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 

在bash脚本中使用:

 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 

要匹配JUST整数,使用这个:

 # change check line to: check=`echo "$1" | grep -E ^\-?[0-9]+$` 

老问题,但我只是想解决我的解决scheme。 这个不需要任何奇怪的shell技巧,也不需要依赖那些永远不会有的东西。

 if [ -n "$(printf '%s\n' "$var" | sed 's/[0-9]//g')" ]; then echo 'is not numeric' else echo 'is numeric' fi 

基本上它只是从input中删除所有的数字,如果你留下一个非零长度的string,那么它不是一个数字。

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

你也可以使用bash的字符类。

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

数字将包括空格,小数点和浮点数的“e”或“E”。

但是,如果指定一个C风格的hex数字,即“0xffff”或“0XFFFF”,则[[:digit:]]返回true。 有一个陷阱在这里,bash允许你做一些像“0xAZ00”,仍然算作一个数字(这不是从海湾合作委员会编译器的一些奇怪的怪癖,让您使用0x符号除16以外的基地? )

在testing之前,如果input是完全不可信的,您可能需要testing“0x”或“0X”,除非您想接受hex数字。 这将通过以下方式完成:

 if [[ ${VARIABLE:1:2} = "0x" ]] || [[ ${VARIABLE:1:2} = "0X" ]]; then echo "$VAR is not numeric"; fi 

我会试试这个:

 printf "%g" "$var" &> /dev/null if [[ $? == 0 ]] ; then echo "$var is a number." else echo "$var is not a number." fi 

注意:这个将nan和inf识别为数字。

不能评论,所以我会添加我自己的答案,这是glenn jackman的答案使用bash模式匹配的扩展。

我最初的需要是识别数字并区分整数和浮点数。 函数定义被扣除为:

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

我使用unit testing(使用shUnit2)来validation我的模式是否按预期工作:

 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 } 

注意:isFloat模式可以修改为对小数点( @(.,) )和E符号( @(Ee) )更加宽容。 我的unit testing只testing整数或浮点值,但不包含任何无效的input。

 [[ $1 =~ ^-?[0-9]+$ ]] && echo "number" 

不要忘记-包括负数!

我用expr 。 如果您尝试向非数字值添加零,它将返回一个非零值:

 if expr $number + 0 > /dev/null 2>&1 then echo "$number is a number" else echo "$number isn't a number" fi 

如果你需要非整数,可以使用bc ,但是我不相信bc具有完全相同的行为。 将零添加到非数字将使您获得零,并返回零值。 也许你可以把bcexpr结合起来。 使用bc将零添加到$number 。 如果答案为0 ,则尝试使用expr来validation$number是否不为零。

 test -z "${i//[0-9]}" && echo digits || echo no no no 

${i//[0-9]}$i中的任何数字replace为空string,请参阅man -P 'less +/parameter\/' bash-z检查结果string的长度是否为零。

如果您还想排除$i为空的情况,则可以使用以下其中一种结构:

 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 

最简单的方法是检查它是否包含非数字字符。 你什么也没有replace所有的数字字符,并检查长度。 如果长度不是一个数字。

 if [[ ! -n ${input//[0-9]/} ]]; then echo "Input Is A Number" fi 

我使用以下(整数):

 ## ##### constants ## ## __TRUE - true (0) ## __FALSE - false (1) ## typeset -r __TRUE=0 typeset -r __FALSE=1 ## -------------------------------------- ## isNumber ## check if a value is an integer ## usage: isNumber testValue ## returns: ${__TRUE} - testValue is a number else not ## function isNumber { typeset TESTVAR="$(echo "$1" | sed 's/[0-9]*//g' )" [ "${TESTVAR}"x = ""x ] && return ${__TRUE} || return ${__FALSE} } isNumber $1 if [ $? -eq ${__TRUE} ] ; then print "is a number" fi 

我尝试了超刀刃的配方,因为它对我来说似乎是最实用的,并且不能使它工作。 最后,我devise了另一种方式,基于其他参数replace,这次用正则expression式replace:

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

它删除$ var中的每个:digit:类字符,并检查是否剩下一个空string,这意味着原来只有数字。

我喜欢这一个是它的小脚印和灵活性。 在这种forms下,它只适用于非分隔的,基数为10的整数,当然你可以使用模式匹配来适应其他需求。

@charles Dufy和其他人已经给出了明确的答案。 纯粹的bash解决scheme将使用以下内容:

 string="-12,345" if [[ "$string" =~ ^-?[0-9]+[.,]?[0-9]*$ ]] then echo $string is a number else echo $string is not a number fi 

尽pipe对于实数,在小数点之前不一定要有数字。

为了更全面地支持浮点数和科学记数法(C / Fortran中的许多程序,或者将这种方式导出为浮点数),对这一行的有用补充如下:

 string="1.2345E-67" if [[ "$string" =~ ^-?[0-9]*[.,]?[0-9]*[eE]?-?[0-9]+$ ]] then echo $string is a number else echo $string is not a number fi 

因此导致一种区分数字types的方法,如果您正在寻找任何特定types:

 string="-12,345" if [[ "$string" =~ ^-?[0-9]+$ ]] then echo $string is an integer elif [[ "$string" =~ ^-?[0-9]*[.,]?[0-9]*$ ]] then echo $string is a float elif [[ "$string" =~ ^-?[0-9]*[.,]?[0-9]*[eE]-?[0-9]+$ ]] then echo $string is a scientific number else echo $string is not a number fi 

注意:我们可以列出十进制和科学记数法的语法要求,一种是允许逗号作为基数点,另一种是“。”。 然后我们会断言只能有一个这样的小数点。 在[Ee]浮动中可以有两个+/-符号。 我从Aulu的作品中学到了更多的规则,并对“0-0”这样的糟糕的string进行了testing。 这里是我的正则expression式/ substring / expr工具似乎举行:

 parse_num() { local r=`expr "$1" : '.*\([.,]\)' 2>/dev/null | tr -d '\n'` nat='^[+-]?[0-9]+[.,]?$' \ dot="${1%[.,]*}${r}${1##*[.,]}" \ float='^[\+\-]?([.,0-9]+[Ee]?[-+]?|)[0-9]+$' [[ "$1" == $dot ]] && [[ "$1" =~ $float ]] || [[ "$1" =~ $nat ]] } # usage: parse_num -123.456 

因为我最近不得不篡改这个,最喜欢karttu的unit testing。 我修改了代码,并添加了一些其他的解决scheme,自己尝试一下,看看结果:

 #!/bin/bash # N={0,1,2,3,...} by syntaxerror function isNaturalNumber() { [[ ${1} =~ ^[0-9]+$ ]] } # Z={...,-2,-1,0,1,2,...} by karttu function isInteger() { [[ ${1} == ?(-)+([0-9]) ]] } # Q={...,-½,-¼,0.0,¼,½,...} by karttu function isFloat() { [[ ${1} == ?(-)@(+([0-9]).*([0-9])|*([0-9]).+([0-9]))?(E?(-|+)+([0-9])) ]] } # R={...,-1,-½,-¼,0.E+n,¼,½,1,...} function isNumber() { isNaturalNumber $1 || isInteger $1 || isFloat $1 } bools=("TRUE" "FALSE") 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" false_values="blah meh mooh blah5 67mooh a123bc" for value in ${int_values} ${float_values} ${false_values} do printf " %5s=%-30s" $(isNaturalNumber $value) ${bools[$?]} $(printf "isNaturalNumber(%s)" $value) printf "%5s=%-24s" $(isInteger $value) ${bools[$?]} $(printf "isInteger(%s)" $value) printf "%5s=%-24s" $(isFloat $value) ${bools[$?]} $(printf "isFloat(%s)" $value) printf "%5s=%-24s\n" $(isNumber $value) ${bools[$?]} $(printf "isNumber(%s)" $value) done 

所以isNumber()包括破折号,逗号和指数表示法,因此在整数和浮点数上返回TRUE,而在另一方面,返回FALSE和isInteger()在浮点数上返回FALSE。 为了您的方便,所有的作为一个衬里:

 isNaturalNumber() { [[ ${1} =~ ^[0-9]+$ ]]; } isInteger() { [[ ${1} == ?(-)+([0-9]) ]]; } isFloat() { [[ ${1} == ?(-)@(+([0-9]).*([0-9])|*([0-9]).+([0-9]))?(E?(-|+)+([0-9])) ]]; } isNumber() { isNaturalNumber $1 || isInteger $1 || isFloat $1; } 

这不是直接回答OP的问题,但也许还是有用的。 你可以使用declare来强制variables的types。 使用declare -i来确保variables的值是一个整数。

例如:

 $ declare -i somevar1=2189 $ echo $somevar1 2189 $ declare -i somevar2=notanumber $ echo $somevar2 0 

由于shellalgorithm是在赋值时应用的,当variables被赋值为非整数时,它将返回一个空值。 空值被评估为0。

参考文献:

  • Bash Builtins
  • Shell算术

快速和肮脏:我知道这不是最优雅的方式,但我通常只是添加一个零来testing结果。 像这样:

 function isInteger { [ $(($1+0)) != 0 ] && echo "$1 is a number" || echo "$1 is not a number" } x=1; isInteger $x x="1"; isInteger $x x="joe"; isInteger $x x=0x16 ; isInteger $x x=-32674; isInteger $x 

$(($ 1 + 0))将返回0或炸弹如果$ 1不是一个整数。 例如:

 function zipIt { # quick zip - unless the 1st parameter is a number ERROR="not a valid number. " if [ $(($1+0)) != 0 ] ; then # isInteger($1) echo " backing up files changed in the last $1 days." OUT="zipIt-$1-day.tgz" find . -mtime -$1 -type f -print0 | xargs -0 tar cvzf $OUT return 1 fi showError $ERROR } 

注:我想我从来没有想过检查浮动或混合types,这将使​​整个脚本炸弹……在我的情况下,我不希望它进一步。 我要玩弄mrucci的解决scheme和Duffy的正则expression式 – 它们似乎是bash框架内最强大的…

我发现相当短的版本:

 function isnum() { return `echo "$1" | awk -F"\n" '{print ($0 != $0+0)}'` } 
  • variables来检查

    number=12345number=-23234number=23.167number=-345.234

  • 检查数字或非数字

    echo $number | grep -E '^-?[0-9]*\.?[0-9]*$' > /dev/null

  • 根据上述的退出状态决定进一步的行动

    if [ $? -eq 0 ]; then echo "Numeric"; else echo "Non-Numeric"; fi

捕获负数:

 if [[ $1 == ?(-)+([0-9.]) ]] then echo number else echo not a number fi 

你可以像这样使用“let”:

 [ ~]$ var=1 [ ~]$ let $var && echo "It's a number" || echo "It's not a number" It\'sa number [ ~]$ var=01 [ ~]$ let $var && echo "It's a number" || echo "It's not a number" It\'sa number [ ~]$ var=toto [ ~]$ let $var && echo "It's a number" || echo "It's not a number" It\'s not a number [ ~]$ 

但是我更喜欢在这个线程中使用“=〜”Bash 3+运算符。

I use printf as other answers mentioned, if you supply the format string "%f" or "%i" printf will do the checking for you. Easier than reinventing the checks, the syntax is simple and short and printf is ubiquitous. So its a decent choice in my opinion – you can also use the following idea to check for a range of things, its not only useful for checking numbers.

 declare -r CHECK_FLOAT="%f" declare -r CHECK_INTEGER="%i" ## <arg 1> Number - Number to check ## <arg 2> String - Number type to check ## <arg 3> String - Error message function check_number() { local NUMBER="${1}" local NUMBER_TYPE="${2}" local ERROR_MESG="${3}" local -i PASS=1 local -i FAIL=0 case "${NUMBER_TYPE}" in "${CHECK_FLOAT}") if ((! $(printf "${CHECK_FLOAT}" "${NUMBER}" &>/dev/random;echo $?))); then echo "${PASS}" else echo "${ERROR_MESG}" 1>&2 echo "${FAIL}" fi ;; "${CHECK_INTEGER}") if ((! $(printf "${CHECK_INTEGER}" "${NUMBER}" &>/dev/random;echo $?))); then echo "${PASS}" else echo "${ERROR_MESG}" 1>&2 echo "${FAIL}" fi ;; *) echo "Invalid number type format: ${NUMBER_TYPE} to check_number()." 1>&2 echo "${FAIL}" ;; esac } 

>$ var=45

>$ (($(check_number $var "${CHECK_INTEGER}" "Error: Found $var - An integer is required."))) && { echo "$var+5" | bc; }

I like Alberto Zaccagni's answer.

 if [ "$var" -eq "$var" ] 2>/dev/null; then 

Important prerequisites: – no subshells spawned – no RE parsers invoked – most shell applications don't use real numbers

But if $var is complex (eg an associative array access), and if the number will be a non-negative integer (most use-cases), then this is perhaps more efficient?

 if [ "$var" -ge 0 ] 2> /dev/null; then .. 
 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.

Following up on David W's answer from Oct '13, if using expr this might be better

 test_var=`expr $am_i_numeric \* 0` >/dev/null 2>&1 if [ "$test_var" = "" ] then ...... 

If numeric, multiplied by 1 gives you the same value, (including negative numbers). Otherwise you get null which you can test for