测试string是否为有效整数

时间:2010-02-05 20:55:28

标签: string bash if-statement integer

我正在尝试做一些常见的事情:在shell脚本中解析用户输入。如果用户提供了有效的整数,则脚本会执行一项操作,如果无效,则执行其他操作。麻烦的是,我还没有找到一种简单(而且相当优雅)的方式 - 我不想用char来分开char。

我知道这一定很容易,但我不知道怎么做。我可以用十几种语言来做,但不是BASH!

在我的研究中,我发现了这个:

Regular expression to test whether a string consists of a valid real number in base 10

其中有一个关于正则表达式的答案,但据我所知,这是C中的一个函数(等等)。尽管如此,它看起来像是一个很好的答案,所以我用grep尝试了它,但是grep不知道该怎么做。我试过-P在我的盒子上意味着把它当作PERL regexp - nada。 Dash E(-E)也没有用。而且-F。

也没有

为了清楚起见,我正在尝试这样的事情,寻找任何输出 - 从那里,我将破解脚本以利用我得到的任何东西。 (IOW,我希望在有效行重复时,不符合要求的输入不会返回任何内容。)

snafu=$(echo "$2" | grep -E "/^[-+]?(?:\.[0-9]+|(?:0|[1-9][0-9]*)(?:\.[0-9]*)?)$/")
if [ -z "$snafu" ] ;
then
   echo "Not an integer - nothing back from the grep"
else
   echo "Integer."
fi

有人请说明这是最容易做到的吗?

坦率地说,在我看来,这是TEST的缩影。它应该有这样的标志

if [ -I "string" ] ;
then
   echo "String is a valid integer."
else
   echo "String is not a valid integer."
fi

11 个答案:

答案 0 :(得分:166)

[[ $var =~ ^-?[0-9]+$ ]]
  • ^表示输入模式的开头
  • -是文字“ - ”
  • ?表示“前一个(-)”中的0或1
  • +表示“前面的一个或多个([0-9])”
  • $表示输入模式的结束

因此正则表达式匹配可选的-(对于负数的情况),后跟一个或多个十进制数字。

<强>参考

答案 1 :(得分:59)

哇......这里有很多好的解决方案!在上述所有解决方案中,我同意@nortally使用-eq一个班轮是最酷的。

我正在运行GNU bash,版本4.1.5(Debian)。我也在ksh上检查了这个(SunSO 5.10)。

以下是我的检查$1是否为整数的版本:

if [ "$1" -eq "$1" ] 2>/dev/null
then
    echo "$1 is an integer !!"
else
    echo "ERROR: first parameter must be an integer."
    echo $USAGE
    exit 1
fi

这种方法也考虑了负数,其他一些解决方案会产生错误的否定结果,并且它将允许前缀为“+”(例如+30),这显然是一个整数。

结果:

$ int_check.sh 123
123 is an integer !!

$ int_check.sh 123+
ERROR: first parameter must be an integer.

$ int_check.sh -123
-123 is an integer !!

$ int_check.sh +30
+30 is an integer !!

$ int_check.sh -123c
ERROR: first parameter must be an integer.

$ int_check.sh 123c
ERROR: first parameter must be an integer.

$ int_check.sh c123
ERROR: first parameter must be an integer.

Ignacio Vazquez-Abrams提供的解决方案在解释之后也非常整洁(如果你喜欢正则表达式)。但是,它不会使用+前缀处理正数,但可以轻松修复如下:

[[ $var =~ ^[-+]?[0-9]+$ ]]

答案 2 :(得分:25)

此次聚会的迟到者。我非常惊讶,没有一个答案提到最简单,最快,最便携的解决方案; case声明。

case ${variable#[-+]} in
  *[!0-9]* | '') echo Not a number ;;
  * ) echo Valid number ;;
esac

在比较之前修剪任何符号感觉有点像黑客,但这使得case语句的表达式变得更加简单。

答案 3 :(得分:9)

对于预先Bash 3.1的可移植性(引入=~测试时),请使用expr

if expr "$string" : '-\?[0-9]\+$' >/dev/null
then
  echo "String is a valid integer."
else
  echo "String is not a valid integer."
fi

expr STRING : REGEX搜索在STRING开始时锚定的REGEX,回显第一组(或匹配的长度,如果没有)并返回成功/失败。这是旧的正则表达式语法,因此超出\-\?表示“可能-”,[0-9]\+表示“一个或多个数字”,$表示“字符串结束”。

Bash也支持扩展的全局,但我不记得从哪个版本开始。

shopt -s extglob
case "$string" of
    @(-|)[0-9]*([0-9]))
        echo "String is a valid integer." ;;
    *)
        echo "String is not a valid integer." ;;
esac

# equivalently, [[ $string = @(-|)[0-9]*([0-9])) ]]

@(-|)表示“-或无”,[0-9]表示“数字”,*([0-9])表示“零或更多数字”。

答案 4 :(得分:8)

我喜欢使用-eq测试的解决方案,因为它基本上是一行的。

我自己的解决方案是使用参数扩展来丢弃所有数字并查看是否还有其他内容。 (我还在使用3.0,之前没有使用[[expr,但很高兴见到它们。)

if [ "${INPUT_STRING//[0-9]}" = "" ]; then
  # yes, natural number
else
  # no, has non-numeral chars
fi

答案 5 :(得分:3)

这是另一个问题(仅使用test builtin命令及其返回码):

function is_int() { return $(test "$@" -eq "$@" > /dev/null 2>&1); } 

input="-123"

if $(is_int "${input}");
then
   echo "Input: ${input}"
   echo "Integer: $[${input}]"
else
   echo "Not an integer: ${input}"
fi

答案 6 :(得分:2)

您可以删除非数字并进行比较。这是一个演示脚本:

for num in "44" "-44" "44-" "4-4" "a4" "4a" ".4" "4.4" "-4.4" "09"
do
    match=${num//[^[:digit:]]}    # strip non-digits
    match=${match#0*}             # strip leading zeros
    echo -en "$num\t$match\t"
    case $num in
        $match|-$match)    echo "Integer";;
                     *)    echo "Not integer";;
    esac
done

这是测试输出的样子:

44      44      Integer
-44     44      Integer
44-     44      Not integer
4-4     44      Not integer
a4      4       Not integer
4a      4       Not integer
.4      4       Not integer
4.4     44      Not integer
-4.4    44      Not integer
09      9       Not integer

答案 7 :(得分:2)

对我来说,最简单的解决方案是在(())表达式中使用变量,如下所示:

if ((VAR > 0))
then
  echo "$VAR is a positive integer."
fi

当然,只有零值对您的应用程序没有意义时,此解决方案才有效。在我的情况下,这恰好是正确的,这比其他解决方案简单得多。

正如评论中所指出的,这会使您受到代码执行攻击:(( ))运算符评估VAR,如the bash(1) man page Arithmetic Evaluation部分所述}。因此,当VAR的内容来源不确定时,您不应该使用此技术(当然,您也不应该使用任何其他形式的变量扩展)。

答案 8 :(得分:0)

或使用sed:

   test -z $(echo "2000" | sed s/[0-9]//g) && echo "integer" || echo "no integer"
   # integer

   test -z $(echo "ab12" | sed s/[0-9]//g) && echo "integer" || echo "no integer"
   # no integer

答案 9 :(得分:0)

添加Ignacio Vazquez-Abrams的答案。这将允许+符号在整数之前,并且它将允许任意数量的零作为小数点。例如,这将允许+45.00000000被视为整数 但是,$ 1必须格式化为包含小数点。这里45不算是整数,但45.0是。

if [[ $1 =~ ^-?[0-9]+.?[0]+$ ]]; then
    echo "yes, this is an integer"
elif [[ $1 =~ ^\+?[0-9]+.?[0]+$ ]]; then
    echo "yes, this is an integer"
else
    echo "no, this is not an integer"
fi

答案 10 :(得分:0)

对于笑声,我大致只是快速计算出一组函数来执行此操作(is_string,is_int,is_float,是字符串或其他),但是有更高效(更少代码)的方法来执行此操作:

#!/bin/bash

function strindex() {
    x="${1%%$2*}"
    if [[ "$x" = "$1" ]] ;then
        true
    else
        if [ "${#x}" -gt 0 ] ;then
            false
        else
            true
        fi
    fi
}

function is_int() {
    if is_empty "${1}" ;then
        false
        return
    fi
    tmp=$(echo "${1}" | sed 's/[^0-9]*//g')
    if [[ $tmp == "${1}" ]] || [[ "-${tmp}" == "${1}" ]] ; then
        #echo "INT (${1}) tmp=$tmp"
        true
    else
        #echo "NOT INT (${1}) tmp=$tmp"
        false
    fi
}

function is_float() {
    if is_empty "${1}" ;then
        false
        return
    fi
    if ! strindex "${1}" "-" ; then
        false
        return
    fi
    tmp=$(echo "${1}" | sed 's/[^a-z. ]*//g')
    if [[ $tmp =~ "." ]] ; then
        #echo "FLOAT  (${1}) tmp=$tmp"
        true
    else
        #echo "NOT FLOAT  (${1}) tmp=$tmp"
        false
    fi
}

function is_strict_string() {
    if is_empty "${1}" ;then
        false
        return
    fi
    if [[ "${1}" =~ ^[A-Za-z]+$ ]]; then
        #echo "STRICT STRING (${1})"
        true
    else
        #echo "NOT STRICT STRING (${1})"
        false
    fi
}

function is_string() {
    if is_empty "${1}" || is_int "${1}" || is_float "${1}" || is_strict_string "${1}" ;then
        false
        return
    fi
    if [ ! -z "${1}" ] ;then
        true
        return
    fi
    false
}
function is_empty() {
    if [ -z "${1// }" ] ;then
        true
    else
        false
    fi
}

在这里运行一些测试,我定义-44是一个int但是44-不是等..:

for num in "44" "-44" "44-" "4-4" "a4" "4a" ".4" "4.4" "-4.4" "09" "hello" "h3llo!" "!!" " " "" ; do
    if is_int "$num" ;then
        echo "INT = $num"

    elif is_float "$num" ;then
        echo "FLOAT = $num"

    elif is_string "$num" ; then
        echo "STRING = $num"

    elif is_strict_string "$num" ; then
        echo "STRICT STRING = $num"
    else
        echo "OTHER = $num"
    fi
done

输出:

INT = 44
INT = -44
STRING = 44-
STRING = 4-4
STRING = a4
STRING = 4a
FLOAT = .4
FLOAT = 4.4
FLOAT = -4.4
INT = 09
STRICT STRING = hello
STRING = h3llo!
STRING = !!
OTHER =  
OTHER = 

注意:在添加八进制等数字时,前导0可能会推断其他内容,因此如果您打算将'09'视为int(我正在做),最好将其剥离(例如expr 09 + 0或用sed剥离)