测试 string 是否为有效整数

我正在尝试做一些足够常见的事情: 解析 shell 脚本中的用户输入。如果用户提供了一个有效的整数,则脚本执行一件事情,如果无效,则执行其他事情。问题是,我还没有找到一种简单(而且相当优雅)的方法来做这件事——我不想一个字符一个字符地把它拆开。

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

在我的研究中,我发现:

正则表达式,用于测试字符串是否由以10为基数的有效实数组成

这里有一个关于正则表达式的答案,但是据我所知,这是 C 语言中的一个函数。尽管如此,它还是有一个看起来不错的答案,所以我用 grep 尝试了一下,但 grep 不知道该怎么处理它。我尝试了 -P,它在我的盒子上的意思是把它当作一个 PERL regexp-nada。破折号 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

有人能解释一下这是怎么做到的吗?

坦率地说,这是一个测试的缺点,在我看来。它应该有一个这样的旗帜

if [ -I "string" ] ;
then
echo "String is a valid integer."
else
echo "String is not a valid integer."
fi
180740 次浏览
[[ $var =~ ^-?[0-9]+$ ]]
  • ^表示输入模式的开始
  • -是字面上的“-”
  • ?表示“前面(-)中的0或1”
  • +表示“前述([0-9])中的1个或多个”
  • $表示输入模式的结束

因此,正则表达式匹配一个可选的 -(对于负数) ,后跟一个或多个小数位。

参考文献 :

为了便于在 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 还支持扩展的 globs,尽管我不记得是从哪个版本开始的。

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])表示“零位或多位”。

你可以去掉非数字并进行比较,这里有一个演示脚本:

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

下面是另外一个例子(只使用测试内置命令及其返回代码) :

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

input="-123"
 

if is_int "$input"
then
echo "Input: ${input}"
echo "Integer: ${input}"
else
echo "Not an integer: ${input}"
fi

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

我自己的解决方案是使用参数展开来丢弃所有的数字,看看是否还有剩余。(我还在使用3.0,以前没有使用过 [[或者 expr,但是很高兴见到它们。)

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

或与 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

来晚了。我非常惊讶没有一个答案提到最简单、最快速、最便携的解决方案: case语句。

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

在比较之前对任何符号进行修剪看起来有点像一种技巧,但是这使 case 语句的表达式变得非常简单。

哇... 这里有这么多好的解决方案! !在以上所有的解决方案中,我同意@正常情况下使用 -eq一行程序是最酷的。

我正在运行 GNU bash,版本 4.1.5(Debian)。

以下是我检查 $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 提供的解决方案也非常简洁(如果您喜欢 regex)。但是,它不处理带有 +前缀的正数,但是它可以很容易地固定如下:

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

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

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

当然,这种解决方案只有在应用程序无法使用零值时才有效。这在我的例子中是正确的,这比其他的解决方案要简单得多。

正如注释中指出的,这可能使您受到代码执行攻击: (( ))操作符计算 VAR,如 Bash (1)手册页Arithmetic Evaluation部分所述。因此,当 VAR内容的来源不确定时,不应该使用这种技术(当然,也不应该使用任何其他形式的变量扩展)。

来自 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

为了搞笑起见,我粗略地设计了一组函数(is _ string,is _ int,is _ float,is alpha string,或者其他) ,但是还有更有效(更少代码)的方法:

#!/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’当作一个整型数(我正在这样做)(例如 expr 09 + 0或带有 sed) ,最好去掉它们