如何测试一个变量是否是Bash中的数字?

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

我只想做这样的事情:

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

任何帮助?

928608 次浏览

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

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

如果该值不一定是整数,请考虑适当地修改正则表达式;例如:

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

…或者,用符号处理数字:

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

这是一个有点粗糙的边缘,但更多的新手友好。

if [ $number -ge 0 ]thenecho "Continue with code block"elseecho "We matched 0 or $number is not a number"fi

这将导致一个错误并打印“非法数字:”如果$num不是一个数字,但它不会脱离脚本。奇怪的是,我找不到一个测试选项来测试一个整数。这里的逻辑将匹配任何大于或等于0的数字。

以下解决方案也可以在基本shell中使用,例如Bourne,而不需要正则表达式。基本上,任何使用非数字的数值求值操作都将导致错误,该错误将在shell中被隐式视为false:

"$var" -eq "$var"

如:

#!/bin/bash
var=a
if [ -n "$var" ] && [ "$var" -eq "$var" ] 2>/dev/null; thenecho numberelseecho not a numberfi

您还可以测试$?操作的返回代码更明确:

[ -n "$var" ] && [ "$var" -eq "$var" ] 2>/dev/nullif [ $? -ne 0 ]; thenecho $var is not numberfi

标准错误的重定向是为了隐藏bash打印出的“预期整数表达式”消息,以防我们没有数字。

洞穴(感谢下面的评论):

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

我使用以下(对于整数):

## ##### constants#### __TRUE - true (0)## __FALSE - false (1)##typeset -r __TRUE=0typeset -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 $1if [ $? -eq ${__TRUE} ] ; thenprint "is a number"fi

这个测试一个数字是否是一个非负整数。它是shell独立的(即没有大写字母),并且只使用shell内置:

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

该答案的先前版本提出:

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

但这是不正确,因为它接受任何以数字开头的字符串,如jilles建议

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

您还可以使用bash的字符类。

if [[ $VAR = *[[:digit:]]* ]]; thenecho "$VAR is numeric"elseecho "$VAR is not numeric"fi

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

但是,如果您指定一个C风格的十六进制数,即“0xffff”或“0XFFFF”,[[: digit:]]返回true。这里有点陷阱,bash允许您执行类似“0xAZ00”的操作并仍然将其计为数字(这不是来自GCC编译器的一些奇怪怪癖,允许您对16以外的基数使用0x表示法吗???)

如果您的输入完全不受信任,您可能希望在测试它是否是数字之前测试“0x”或“0X”,除非您想接受十六进制数字。这将通过以下方式实现:

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

我尝试了超声波刀片的配方,因为它对我来说似乎是最实用的,但无法使其工作。最后,我设计了另一种方法,基于其他参数替换,这次使用正则表达式替换:

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

它删除了$var中的每个: digit: class字符,并检查我们是否留下了一个空字符串,这意味着原始字符串只是数字。

我喜欢这个的地方是它的占用空间小和灵活性。在这种形式下,它只适用于无定界的、以10为基的整数,尽管您肯定可以使用模式匹配来满足其他需求。

没有巴什主义(即使在系统V sh中也有效),

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

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

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

快速和肮脏:我知道这不是最优雅的方式,但我通常只是给它加一个零并测试结果。

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

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

function zipIt  { # quick zip - unless the 1st parameter is a numberERROR="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 $OUTreturn 1fishowError $ERROR}

注意:我想我从来没有想过要检查会使整个脚本炸弹的浮点数或混合类型……在我的情况下,我不想让它走得更远。我将使用mrucci的解决方案和Duffy的regex——它们似乎是bash框架中最健壮的……

下面是我写的脚本,用于与Nagios的脚本集成,到目前为止它一直正常工作

#!/bin/bash# Script to test variable is numeric or not# Shirish Shukla# Pass arg1 as numbera1=$1a=$(echo $a1|awk '{if($1 > 0) print $1; else print $1"*-1"}')b=$(echo "scale=2;$a/$a + 1" | bc -l 2>/dev/null)if [[ $b > 1 ]]thenecho "$1 is Numeric"elseecho "$1 is Non Numeric"fi

EG:

# sh isnumsks.sh   "-22.22"-22.22 is Numeric
# sh isnumsks.sh   "22.22"22.22 is Numeric
# sh isnumsks.sh   "shirish22.22"shirish22.22 is Non  Numeric
[[ $1 =~ ^-?[0-9]+$ ]] && echo "number"

不要忘记-包含负数!

我找到了一个简短的版本:

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

我对直接解析shell中的数字格式的解决方案感到惊讶。shell并不适合这种情况,因为它是用于控制文件和进程的DSL。下面有足够的数字解析器,例如:

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更改为您需要的任何特定格式。

  • 要检查的变量

    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

我会试试这个:

printf "%g" "$var" &> /dev/nullif [[ $? == 0 ]] ; thenecho "$var is a number."elseecho "$var is not a number."fi

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

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

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

或使用一个POSIX字符类

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

获取负数:

if [[ $1 == ?(-)+([0-9.]) ]]thenecho numberelseecho not a numberfi

我在看答案,然后…发现没有人想到浮点数(带点)!

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

要直接在命令行中测试:

$ 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 numericecho "Yeap!"else# it is NOT numeric.echo "nooop"fi

要匹配只是整数,请使用以下命令:

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

老问题,但我只是想坚持我的解决方案。这个不需要任何奇怪的贝壳技巧,也不依赖于从未存在过的东西。

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

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

我使用exr。如果您尝试将零添加到非数值,它将返回非零:

if expr -- "$number" + 0 > /dev/null 2>&1thenecho "$number is a number"elseecho "$number isn't a number"fi

如果你需要非整数,也许可以使用bc,但我不相信bc有完全相同的行为。将零添加到非数字会使你为零,它也返回零的值。也许你可以组合bcexpr。使用bc将零添加到$number。如果答案是0,然后尝试expr来验证$number不是零。

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

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

但我更喜欢使用“=~"Bash 3+运算符,就像这个线程中的一些答案一样。

只是跟进@玛丽。但是因为我没有足够的代表,不能把这个作为对那篇文章的评论。不管怎样,这是我用的:

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

如果参数是数字,该函数将返回“1”,否则将返回“0”。这适用于整数和浮点数。用法类似于:

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

接受的答案在这里不起作用,我在MacOS上。以下代码有效:

if [ $(echo "$number" | grep -c '^[0-9]\+$') = 0 ]; thenecho "it is a number"elseecho "not a number"fi
test -z "${i//[0-9]}" && echo digits || echo no no no

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

如果您还想排除$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

查尔斯·杜菲等人已经给出了明确的答案。纯bash解决方案将使用以下内容:

string="-12,345"if [[ "$string" =~ ^-?[0-9]+[.,]?[0-9]*$ ]]thenecho $string is a numberelseecho $string is not a numberfi

虽然对于实数,在基点之前有一个数字并不是强制性的。

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

string="1.2345E-67"if [[ "$string" =~ ^-?[0-9]*[.,]?[0-9]*[eE]?-?[0-9]+$ ]]thenecho $string is a numberelseecho $string is not a numberfi

因此,如果您正在寻找任何特定类型,则可以找到区分数字类型的方法:

string="-12,345"if [[ "$string" =~ ^-?[0-9]+$ ]]thenecho $string is an integerelif [[ "$string" =~ ^-?[0-9]*[.,]?[0-9]*$ ]]thenecho $string is a floatelif [[ "$string" =~ ^-?[0-9]*[.,]?[0-9]*[eE]-?[0-9]+$ ]]thenecho $string is a scientific numberelseecho $string is not a numberfi

注意:我们可以列出十进制和科学记数法的语法要求,其中之一是允许逗号作为基数点,以及“.”。然后我们会断言必须只有一个这样的基数点。[Ee]浮点中可以有两个+/-符号。我从Aulu的工作中学到了更多的规则,并对 '' '-' '-E-1''0-0'等糟糕的字符串进行了测试。这是我的正则表达式/子字符串/exr工具,它们似乎正在发挥作用:

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

我使用printf作为提到的其他答案,如果您提供格式字符串“%f”或“%i”,printf将为您进行检查。比重新发明检查更容易,语法简单而简短,printf无处不在。所以在我看来,这是一个不错的选择-你也可以使用以下想法来检查一系列事情,它不仅对检查数字有用。

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 messagefunction check_number() {local NUMBER="${1}"local NUMBER_TYPE="${2}"local ERROR_MESG="${3}"local -i PASS=1local -i FAIL=0case "${NUMBER_TYPE}" in"${CHECK_FLOAT}")if ((! $(printf "${CHECK_FLOAT}" "${NUMBER}" &>/dev/random;echo $?))); thenecho "${PASS}"elseecho "${ERROR_MESG}" 1>&2echo "${FAIL}"fi;;"${CHECK_INTEGER}")if ((! $(printf "${CHECK_INTEGER}" "${NUMBER}" &>/dev/random;echo $?))); thenecho "${PASS}"elseecho "${ERROR_MESG}" 1>&2echo "${FAIL}"fi;;*)echo "Invalid number type format: ${NUMBER_TYPE} to check_number()." 1>&2echo "${FAIL}";;esac}

>$ var=45

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

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

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

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

我使用单元测试(使用shUnit2)来验证我的模式是否按预期工作:

oneTimeSetUp() {int_values="0 123 -0 -123"float_values="0.0 0. .0 -0.0 -0. -.0 \123.456 123. .456 -123.456 -123. -.456123.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 valuefor value in ${int_values}doassertTrue "${value} should be tested as integer" "isInteger ${value}"assertFalse "${value} should not be tested as float" "isFloat ${value}"done
for value in ${float_values}doassertTrue "${value} should be tested as float" "isFloat ${value}"assertFalse "${value} should not be tested as integer" "isInteger ${value}"done
}

注意:isFloat模式可以修改为对小数点(@(.,))和E符号(@(Ee))更宽容。我的单元测试仅测试整数或浮点值,但不测试任何无效输入。

一种简单的方法是检查它是否包含非数字字符。您将所有数字字符替换为空并检查长度。如果有长度,它就不是数字。

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

我喜欢Alberto Zaccagni的回答。

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

重要的先决条件:-没有生成子壳-没有调用RE解析器-大多数shell应用程序不使用实数

但是如果$var是复杂的(例如关联数组访问),并且如果数字将是一个非负整数(大多数用例),那么这可能更有效?

if [ "$var" -ge 0 ] 2> /dev/null; then ..
printf '%b' "-123\nABC" | tr '[:space:]' '_' | grep -q '^-\?[[:digit:]]\+$' && echo "Integer." || echo "NOT integer."

如果您不接受负整数,请删除grep匹配模式中的-\?

从10月13日开始跟进David W的回答,如果使用expr可能会更好

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

如果是数字,则乘以1得到相同的值(包括负数)。否则,您会得到null,您可以测试它

由于我最近不得不篡改这个,并且最喜欢单元测试的karttu的 appoach。我修改了代码并添加了一些其他解决方案,自己尝试看看结果:

#!/bin/bash
# N={0,1,2,3,...} by syntaxerrorfunction isNaturalNumber(){[[ ${1} =~ ^[0-9]+$ ]]}# Z={...,-2,-1,0,1,2,...} by karttufunction isInteger(){[[ ${1} == ?(-)+([0-9]) ]]}# Q={...,-½,-¼,0.0,¼,½,...} by karttufunction 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}doprintf "  %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,而另一方面isFloat()在整数值上返回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; }

Stack弹出消息问我是否真的想在30+答案后回答?但当然!!!使用bash新功能,这里是:(评论后我做了一个更改)

函数是Int() { ([[ $1-eq$((1美元+0 )) ]] 2>/dev/null&&[[1美元 ! = '' ]] &;&echo 1)||echo '' }

function isInt() {([[ $1 =~ ^[-+0-9]+$  ]] && [[ $1 -eq $(( $1 + 0 )) ]] 2>/dev/null && [[ $1 != '' ]] && echo 1) || echo ''}

支持:

===============out-of-the-box==================1. negative integers (true & arithmetic),2. positive integers (true & arithmetic),3. with quotation (true & arithmetic),4. without quotation (true & arithmetic),5. all of the above with mixed signs(!!!) (true & arithmetic),6. empty string (false & arithmetic),7. no value (false & arithmetic),8. alphanumeric (false & no arithmetic),9. mixed only signs (false & no arithmetic),================problematic====================10. positive/negative floats with 1 decimal (true & NO arithmetic),11. positive/negative floats with 2 or more decimals (FALSE & NO arithmetic).

True/false是您从函数仅在使用中获得的结果,与[[ $( isInt <arg> ) ]]中的进程替换相结合,因为bash中没有逻辑类型,函数的返回值也没有。

当测试表达式的结果是错误的时,我使用大写,而它应该是相反的!

通过“算术”,我的意思是bash可以做数学像这个表达式:$x=$(( $y + 34))

在数学表达式中,当参数按照预期的方式工作时,我使用'arithmetic/no arithmetic',当它与预期相比行为不端时,我使用'NO arithmetic'。

如你所见,只有10和11是有问题的!

完美!

PS:注意案例9中的大多数流行答案失败

在这里用正则表达式做同样的事情,测试整个部分和小数部分,用点分隔。

re="^[0-9]*[.]{0,1}[0-9]*$"
if [[ $1 =~ $re ]]thenecho "is numeric"elseecho "Naahh, not numeric"fi

这可以通过使用grep来查看有问题的变量是否与扩展的正则表达式匹配来实现。

测试整数1120

yournumber=1120if echo "$yournumber" | grep -qE '^[0-9]+$'; thenecho "Valid number."elseecho "Error: not a number."fi

输出:Valid number.

测试非整数1120a

yournumber=1120aif echo "$yournumber" | grep -qE '^[0-9]+$'; thenecho "Valid number."elseecho "Error: not a number."fi

输出:Error: not a number.


补充说明

  • grep-E开关允许我们使用扩展正则表达式'^[0-9]+$'。这个正则表达式意味着变量应该只有[]包含从变量的^开始到$结束的数字0-9零到九,并且应该至少有+一个字符。
  • grep-q安静开关关闭任何输出,无论它是否找到任何东西。
  • if检查grep的退出状态。退出状态0表示成功,任何更大的表示错误。如果找到匹配项,grep命令的退出状态为0,如果没有匹配项,退出状态为1

所以把这一切放在一起,在if测试中,我们echo变量$yournumber|将其管道传输到grep-q开关静默匹配-E扩展正则表达式'^[0-9]+$'表达式。如果grep成功找到匹配,grep的退出状态将是0,如果没有,echo1。如果成功匹配,我们echo2。如果匹配失败,我们echo3。


对于浮动或双打

对于浮点数或双精度,我们可以将正则表达式从'^[0-9]+$'更改为'^[0-9]*\.?[0-9]+$'

测试浮点数1120.01

yournumber=1120.01if echo "$yournumber" | grep -qE '^[0-9]*\.?[0-9]+$'; thenecho "Valid number."elseecho "Error: not a number."fi

输出:Valid number.

测试浮点数11.20.01

yournumber=11.20.01if echo "$yournumber" | grep -qE '^[0-9]*\.?[0-9]+$'; thenecho "Valid number."elseecho "Error: not a number."fi

输出:Error: not a number.


对于负片

要允许负整数,只需将正则表达式从'^[0-9]+$'更改为'^\-?[0-9]+$'

要允许负浮点数或双精度,只需将正则表达式从'^[0-9]*\.?[0-9]+$'更改为'^\-?[0-9]*\.?[0-9]+$'

对于我的问题我只需要确保用户不会不小心输入一些文本因此我试图保持简单和可读性

isNumber() {(( $1 )) 2>/dev/null}

根据男士页面,这几乎符合我的要求

如果表达式的值非零,则返回状态为0

为了防止“可能是数字”的字符串出现令人讨厌的错误消息,我忽略了错误输出

$ (( 2s ))bash: ((: 2s: value too great for base (error token is "2s")

接受的答案在所有情况下都不适合我BASH 4+所以:

# -- is var an integer? --# trim leading/trailing whitespace, then check for digits return 0 or 1# Globals: None# Arguments: string# Returns: boolean# --is_int() {str="$(echo -e "${1}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"case ${str} in ''|*[!0-9]*) return 1 ;; esacreturn 0}

如何使用它?

有效(将返回0=true):

is_int "100" && echo "return 0" || echo "return 1"

无效(将返回1=false):

is_int "100abc" && echo "returned 0" || echo "returned 1"is_int ""  && echo "returned 0" || echo "returned 1"is_int "100 100"  && echo "returned 0" || echo "returned 1"is_int "      "  && echo "returned 0" || echo "returned 1"is_int $NOT_SET_VAR  && echo "returned 0" || echo "returned 1"is_int "3.14"   && echo "returned 0" || echo "returned 1"

输出:

returned 0returned 1returned 1returned 1returned 1returned 1returned 1

注意,在Bash中,1=false,0=true。我只是简单地打印出来,而像这样的东西更有可能:

if is_int ${total} ; then# perform some actionfi

一些性能和兼容性提示

对于不同类型的测试,有一些截然不同的方法。

我回顾了大多数相关的方法并建立了这个比较。

无符号整数is_uint()

这些函数实现代码来评估表达式是否为无符号整数,即完全由数字组成。

  • 使用参数展开

    (这是我之前的做法!)

    isuint_Parm() { [ "$1" ] && [ -z "${1//[0-9]}" ] ;}
  • 使用分叉到#0

    isuint_Grep() { grep -qE '^[0-9]+$' <<<"$1"; }

    我只测试了这个方法一次,因为它很慢。这只是为了告诉你不要做什么。

  • 使用整数能力

    isuint_Bash() { (( 10#$1 >= 0 )) 2>/dev/null ;}

    或更好:

    isuint_Bash() { set -- ${1//[+-]/.};(( 10#$1 >= 0 )) 2>/dev/null ;}
  • 使用#0

    isuint_Case() { case $1 in ''|*[!0-9]*) return 1;;esac;}
  • 使用<强>巴什的正则表达式

    isuint_Regx() { [[ $1 =~ ^[0-9]+$ ]] ;}

有符号整数is_int()

这些函数实现代码来评估表达式是否为有符号整数,即如上所述,但允许在数字之前使用可选符号。

  • 使用参数展开

    isint_Parm() { local chk=${1#[+-]}; [ "$chk" ] && [ -z "${chk//[0-9]}" ] ;}
  • 使用整数能力

    isint_Bash() {  set -- "${1//[!+-]}" ${1#${1//[!+-]}};(( ( 0 ${1:-+} 10#$2 ) ? 1:1 )) 2>/dev/null ;}
  • 使用#0

    isint_Case() { case ${1#[-+]} in ''|*[!0-9]*) return 1;;esac;}
  • 使用<强>巴什的正则表达式

    isint_Regx() { [[ $1 =~ ^[+-]?[0-9]+$ ]] ;}

数字(无符号浮点数)is_num()

这些函数实现代码来评估表达式是否为浮点数,即如上所述,但允许可选的小数点及其后的其他数字。这并不试图涵盖科学记数法中的数字表达式(例如1.0234E-12)。

  • 使用参数展开

    isnum_Parm() { local ck=${1#[+-]};ck=${ck/.};[ "$ck" ]&&[ -z "${ck//[0-9]}" ];}
  • 使用<强>巴什的正则表达式

    isnum_Regx() { [[ $1 =~ ^[+-]?([0-9]+([.][0-9]*)?|\.[0-9]+)$ ]] ;}
  • 使用#0

    isnum_Case() { case ${1#[-+]} in ''|.|*[!0-9.]*|*.*.*) return 1;; esac ;}

概念测试

(您可以在之前声明的函数之后复制/粘贴此测试代码。)

testcases=(0  1 42 -3 +42 +3. .9 3.14 +3.141 -31.4 '' . 3-3 3.1.4 3a a3 blah 'Good day!');printf '%-12s %4s %4s %4s %4s %4s %4s %4s %4s %4s %4s %4s %4s\n' Value\\Func \U{Prm,Grp,Bsh,Cse,Rgx} I{Prm,Bsh,Cse,Rgx} N{Prm,Cse,Rgx};\for var in "${testcases[@]}";dooutstr='';for func in isuint_{Parm,Grep,Bash,Case,Regx} isint_{Parm,Bash,Case,Regx} \isnum_{Parm,Case,Regx};doif $func "$var"; thenoutstr+='   ##'elseoutstr+='   --'fidoneprintf '%-11s %s\n' "$var" "$outstr"done

应输出:

Value\Func   UPrm UGrp UBsh UCse URgx IPrm IBsh ICse IRgx NPrm NCse NRgx0              ##   ##   ##   ##   ##   ##   ##   ##   ##   ##   ##   ##1              ##   ##   ##   ##   ##   ##   ##   ##   ##   ##   ##   ##42             ##   ##   ##   ##   ##   ##   ##   ##   ##   ##   ##   ##-3             --   --   --   --   --   ##   ##   ##   ##   ##   ##   ##+42            --   --   --   --   --   ##   ##   ##   ##   ##   ##   ##+3.            --   --   --   --   --   --   --   --   --   ##   ##   ##.9             --   --   --   --   --   --   --   --   --   ##   ##   ##3.14           --   --   --   --   --   --   --   --   --   ##   ##   ##+3.141         --   --   --   --   --   --   --   --   --   ##   ##   ##-31.4          --   --   --   --   --   --   --   --   --   ##   ##   ##--   --   --   --   --   --   --   --   --   --   --   --.              --   --   --   --   --   --   --   --   --   --   --   --3-3            --   --   --   --   --   --   ##   --   --   --   --   --3.1.4          --   --   --   --   --   --   --   --   --   --   --   --3a             --   --   --   --   --   --   --   --   --   --   --   --a3             --   --   --   --   --   --   --   --   --   --   --   --blah           --   --   --   --   --   --   --   --   --   --   --   --Good day!      --   --   --   --   --   --   --   --   --   --   --   --

我希望!(注:uint_bash似乎不完美!)

性能对比

然后我构建了这个测试函数:

testFunc() {local tests=1000 start=${EPOCHREALTIME//.}for ((;tests--;)) ;do"$1" "$3"doneprintf -v "$2" %u $((${EPOCHREALTIME//.}-start))}percent(){ local p=00$((${1}00000/$2));printf -v "$3" %.2f%% ${p::-3}.${p: -3};}sortedTests() {local func NaNTime NumTime ftyp="$1" nTest="$2" tTest="$3" min i pct linelocal -a order=()shift 3for func ;dotestFunc "${ftyp}_$func" NaNTime "$tTest"testFunc "${ftyp}_$func" NumTime "$nTest"order[NaNTime+NumTime]=${ftyp}_$func\ $NumTime\ $NaNTimedoneprintf '%-12s %11s %11s %14s\n' Function Number NaN Totalmin="${!order[*]}" min=${min%% *}for i in "${!order[@]}";doread -ra line <<<"${order[i]}"percent "$i" "$min" pctprintf '%-12s %9d\U00B5s %9d\U00B5s  %12d\U00B5s  %9s\n' \"${line[@]}" "$i" "$pct"done}

我可以这样跑:

sortedTests isuint "This is not a number." 31415926535897932384 \Case Grep Parm Bash Regx ;\sortedTests isint  "This is not a number." 31415926535897932384 \Case Parm Bash Regx ;\sortedTests isnum "This string is clearly not a number..." \3.141592653589793238462643383279502884  Case Parm Regx

在我的主机上,这显示了一些东西,比如:

Function          Number         NaN          Totalisuint_Case       6499µs      6566µs         13065µs    100.00%isuint_Parm      26687µs     31600µs         58287µs    446.13%isuint_Regx      36511µs     40181µs         76692µs    587.00%isuint_Bash      43819µs     40311µs         84130µs    643.93%isuint_Grep    1298265µs   1224112µs       2522377µs  19306.37%
Function          Number         NaN          Totalisint_Case       22687µs     21914µs         44601µs    100.00%isint_Parm       35765µs     34428µs         70193µs    157.38%isint_Regx       36949µs     42319µs         79268µs    177.73%isint_Bash       55368µs     65095µs        120463µs    270.09%
Function          Number         NaN          Totalisnum_Case       23313µs     23446µs         46759µs    100.00%isnum_Parm       35677µs     42169µs         77846µs    166.48%isnum_Regx       51864µs     69502µs        121366µs    259.56%

您可以下载完整的isnum比较脚本在这里完整的isnum比较脚本作为文本在这里。(使用UTF8和LATIN处理)。

结论

  • case显然是最快的!比regex快3倍,比参数展开快2倍。
  • <强>叉子(到grep或任何二进制文件)在不需要时应避免。

case方法成为我的首选:

is_uint() { case $1        in '' | *[!0-9]*              ) return 1;; esac ;}is_int()  { case ${1#[-+]} in '' | *[!0-9]*              ) return 1;; esac ;}is_unum() { case $1        in '' | . | *[!0-9.]* | *.*.* ) return 1;; esac ;}is_num()  { case ${1#[-+]} in '' | . | *[!0-9.]* | *.*.* ) return 1;; esac ;}

关于兼容性

为此,我写了一个基于以前测试的小测试脚本,其中:

for shell in bash dash 'busybox sh' ksh zsh "$@";doprintf "%-12s  " "${shell%% *}"$shell < <(testScript) 2>&1 | xargsdone

这表明:

bash          Successdash          Successbusybox       Successksh           Successzsh           Success

我知道其他基于9;bash&9;" aria-label="show questions tagged &9;bash&9;" rel="tag" aria-labelledby="bash-container">bash的解决方案,如正则表达式9;bash&9;" aria-label="show questions tagged &9;bash&9;" rel="tag" aria-labelledby="bash-container">bash的整数在许多其他shell中不起作用,<强>叉子资源昂贵,我更喜欢case的方式(就在参数展开之前,它也基本兼容)。

语法几乎如你所愿。只需要一个函数isnumber

#!/usr/bin/bash
isnumber(){num=$1if [ -z "${num##*[!0-9]*}" ];then return 1elsereturn 0fi}
$(isnumber $1) && VAR=$1 || echo "need a number";echo "VAR is $VAR"

测试:

$ ./isnumtest 10VAR is 10$ ./isnumtest abc10need a numberVAR is

易于理解和兼容的解决方案,使用test命令:

test $myVariable -eq 0 2>/dev/nullif [ $? -le 1 ]; then echo 'ok'; else echo 'KO'; fi

如果myVariable=0,则返回代码为0
如果myVariable>0,则返回代码为1
如果myVariable不是整数,则返回代码为2