Linux 如何在Bash中测试变量是否为数字?

Linux 如何在Bash中测试变量是否为数字?,linux,bash,shell,Linux,Bash,Shell,我只是不知道如何确保传递给脚本的参数是否为数字 我想做的就是这样: test *isnumber* $1 && VAR=$1 || echo "need a number" 有什么帮助吗?一种方法是使用正则表达式,如下所示: re='^[0-9]+$' if ! [[ $yournumber =~ $re ]] ; then echo "error: Not a number" >&2; exit 1 fi 如果值不一定是整数,考虑适当修改正则表达式;例如

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

我想做的就是这样:

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

有什么帮助吗?

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

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

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

^[0-9]+([.][0-9]+)?$
…或者,用符号处理数字:

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

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

"$var" -eq "$var"
例如:

#!/bin/bash

var=a

if [ -n "$var" ] && [ "$var" -eq "$var" ] 2>/dev/null; then
  echo number
else
  echo not a number
fi
您还可以测试$?更明确的操作返回代码:

[ -n "$var" ] && [ "$var" -eq "$var" ] 2>/dev/null
if [ $? -ne 0 ]; then
   echo $var is not number
fi
标准错误的重定向是为了隐藏bash打印出来的“integer expression expected”消息,以防我们没有数字

if [[ ! -n ${input//[0-9]/} ]]; then
    echo "Input Is A Number"
fi
注意事项(感谢下面的评论):

  • 带小数点的数字未标识为有效的“数字”
  • 使用
    [[]]
    而不是
    []
    将始终计算为
    true
  • 大多数非Bash shell总是将此表达式计算为
    true
  • Bash中的行为没有文档记录,因此可能会在没有警告的情况下更改
  • 如果该值包含数字后面的空格(例如“1 a”)会产生错误,例如
    bash:[:1 a:表达式中的语法错误(错误标记为“a”)
  • 如果该值与var name相同(例如i=“i”),则会产生错误,如
    bash:[:i:表达式递归级别超出(错误标记为“i”)
我使用以下(整数):


这将测试一个数字是否为非负整数,是否既与shell无关(即没有bashisms),又只使用shell内置:

不正确。

因为第一个答案(如下)允许包含字符的整数,只要第一个不是变量中的第一个

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

正如在本文中所评论和建议的,这是使用shell模式进行此操作的正确方法

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

您还可以使用bash的角色类

if [[ $VAR = *[[:digit:]]* ]]; then
 echo "$VAR is numeric"
else
 echo "$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

我尝试了ultrasawblade的配方,因为它对我来说似乎是最实用的,但没有成功。尽管如此,最后我还是设计了另一种方法,与其他参数替换方法一样,这次是用正则表达式替换:

[[ "${var//*([[:digit:]])}" ]]; && echo "$var is not numeric" || echo "$var is numeric"
它删除$var中的every:digit:class字符,并检查是否留下一个空字符串,这意味着原始字符串只是数字

我喜欢的是它的占用空间小和灵活性。在这种形式下,它只适用于非分隔的、以10为基数的整数,当然你可以使用模式匹配来满足其他需要。

没有bashisms(即使在System V sh中也能工作)

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


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

快速和肮脏:我知道这不是最优雅的方式,但我通常只是在上面加一个零,然后测试结果。就像这样:

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不是整数,$($1+0))将返回0或bomb。例如:

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
}
注意:我想我从来没有想过检查浮动或混合类型会使整个脚本爆炸…在我的情况下,我不想让它再进一步。我将玩mrucci的解决方案和Duffy的正则表达式-它们似乎是bash框架中最健壮的

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

不要忘了
-
包含负数!

我对直接解析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”更改为您需要的任何特定格式。

我会尝试以下方法:

printf "%g" "$var" &> /dev/null
if [[ $? == 0 ]] ; then
    echo "$var is a number."
else
    echo "$var is not a number."
fi
注意:这将nan和inf识别为数字。

没有人建议bash:

或使用:


要捕获负数,请执行以下操作:

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

我在看答案,然后。。。 意识到没有人想到浮点数(带点)

使用grep也很好。
-E表示扩展regexp
-q表示安静(不回音)
-量化宽松是两者的结合

要直接在命令行中进行测试,请执行以下操作:

$ 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
要仅匹配整数,请使用以下命令:

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

这是一个老问题,但我只是想继续我的解决方案。这个解决方案不需要任何奇怪的外壳技巧,也不需要依赖一些永远不存在的东西

if [ -n "$(printf '%s\n' "$var" | sed 's/[0-9]//g')" ]; then
    echo 'is not numeric'
else
    echo 'is numeric'
fi
基本上,它只是从输入中删除所有数字,如果留下一个非零长度的字符串,那么它就不是一个数字。

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

if [[ ! -n ${input//[0-9]/} ]]; then
    echo "Input Is A Number"
fi
如果表达式--“$number”+0>/dev/null 2>&1
然后
echo“$number是一个数字”
其他的
echo“$number不是一个数字”
fi
如果您需要非整数,则可以使用,但我不认为
bc
具有完全相同的行为。将零添加到非数字会得到零,并且它也会返回零值。也许您可以将
bc
expr
组合使用。使用
bc
将零添加到
# change check line to:
check=`echo "$1" | grep -E ^\-?[0-9]+$`
if [ -n "$(printf '%s\n' "$var" | sed 's/[0-9]//g')" ]; then
    echo 'is not numeric'
else
    echo 'is numeric'
fi
[ ~]$ 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
[ ~]$ 
isnum() { awk -v a="$1" 'BEGIN {print (a == a + 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
test -z "${i//[0-9]}" && echo digits || echo no no no
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
string="-12,345"
if [[ "$string" =~ ^-?[0-9]+[.,]?[0-9]*$ ]]
then
    echo $string is a number
else
    echo $string is not a number
fi
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
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
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
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
} 
function isInteger() {
    [[ ${1} == ?(-)+([0-9]) ]]
}

function isFloat() {
    [[ ${1} == ?(-)@(+([0-9]).*([0-9])|*([0-9]).+([0-9]))?(E?(-|+)+([0-9])) ]]
}
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

}
if [[ ! -n ${input//[0-9]/} ]]; then
    echo "Input Is A Number"
fi
if [ "$var" -eq "$var" ] 2>/dev/null; then
if [ "$var" -ge 0 ] 2> /dev/null; then ..
printf '%b' "-123\nABC" | tr '[:space:]' '_' | grep -q '^-\?[[:digit:]]\+$' && echo "Integer." || echo "NOT integer."
#!/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
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; }
re="^[0-9]*[.]{0,1}[0-9]*$"

if [[ $1 =~ $re ]] 
then
   echo "is numeric"
else
  echo "Naahh, not numeric"
fi
yournumber=1120
if echo "$yournumber" | grep -qE '^[0-9]+$'; then
    echo "Valid number."
else
    echo "Error: not a number."
fi
yournumber=1120a
if echo "$yournumber" | grep -qE '^[0-9]+$'; then
    echo "Valid number."
else
    echo "Error: not a number."
fi
yournumber=1120.01
if echo "$yournumber" | grep -qE '^[0-9]*\.?[0-9]+$'; then
    echo "Valid number."
else
    echo "Error: not a number."
fi
yournumber=11.20.01
if echo "$yournumber" | grep -qE '^[0-9]*\.?[0-9]+$'; then
    echo "Valid number."
else
    echo "Error: not a number."
fi
isNumber() {
    (( $1 )) 2>/dev/null
}
$ (( 2s ))
bash: ((: 2s: value too great for base (error token is "2s")
is_num() { [ "$1" ] && [ -z "${1//[0-9]}" ] ;}
is_num() { 
    local chk=${1#[+-]};
    [ "$chk" ] && [ -z "${chk//[0-9]}" ]
}
is_num() { 
    local chk=${1#[+-]};
    chk=${chk/.}
    [ "$chk" ] && [ -z "${chk//[0-9]}" ]
}
set -- "foo bar"
is_num "$1" && VAR=$1 || echo "need a number"
need a number

set -- "+3.141592"
is_num "$1" && VAR=$1 || echo "need a number"

echo $VAR
+3.141592
cdIs_num() { 
    local re='^[+-]?[0-9]+([.][0-9]+)?$';
    [[ $1 =~ $re ]]
}
if is_num foo;then echo It\'s a number ;else echo Not a number;fi
Not a number
if cdIs_num foo;then echo It\'s a number ;else echo Not a number;fi
Not a number
if is_num 25;then echo It\'s a number ;else echo Not a number;fi
It's a number
if cdIs_num 25;then echo It\'s a number ;else echo Not a number;fi
It's a number
if is_num 3+4;then echo It\'s a number ;else echo Not a number;fi
Not a number
if cdIs_num 3+4;then echo It\'s a number ;else echo Not a number;fi
Not a number
if is_num 3.1415;then echo It\'s a number ;else echo Not a number;fi
It's a number
if cdIs_num 3.1415;then echo It\'s a number ;else echo Not a number;fi
It's a number
time for i in {1..1000};do is_num +3.14159265;done
real    0m2.476s
user    0m1.235s
sys     0m0.000s
time for i in {1..1000};do cdIs_num +3.14159265;done
real    0m4.363s
user    0m2.168s
sys     0m0.000s