如何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
具有完全相同的行为。 将零添加到非数字将使您获得零,并返回零值。 也许你可以把bc
和expr
结合起来。 使用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=12345
或number=-23234
或number=23.167
或number=-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