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

我只想这样做:

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

有什么帮助吗?


当前回答

易于理解和兼容的解决方案,带有测试命令:

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

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

其他回答

要检查的变量号码=12345或号码=-23234或号码=23.167或号码=-345.234检查数字或非数字echo$number|grep-E'^-?[0-9]*\.?[0-9]*$'>/dev/null根据上述退出状态决定进一步行动如果[$?-eq 0];然后回显“数值”;否则回显“非数字”;传真

一些性能和兼容性提示

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

我回顾了最相关的方法并进行了比较。

无符号整数is_uint()

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

使用参数展开(这是我之前的做法!)isuint_Parm(){[“$1”]&&[-z“${1//[0-9]}”];}使用fork进行grepisuint_Grep(){Grep-qE‘^[0-9]+$‘<<“$1”;}我只测试了这个方法一次,因为它非常慢。这只是为了说明不该做什么。使用bash整数功能isuint_Bash(){((10#$1>=0))2>/dev/null;}或更好:isuint_Bash(){set--${1//[+-]/.};((10#$1>=0))2>/dev/null;}用例isuint_Case(){'|*[!0-9]*中的Case$1)return 1;;esac;}使用bash的正则表达式isuint_Regx(){[[$1=~^[0-9]+$]];}

有符号整数is_int()

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

使用参数展开isint_Par(){local chk=${1#[+-]};[“$chk”]&&[-z“${chk//[0-9]}”];}使用bash整数功能isint_Bash(){set--“${1//[!+-]}”${1#${1//[!+-]}};(( ( 0 ${1:-+} 10#$2 ) ? 1:1))2>/dev/null;}用例isint_Case(){Case${1#[-+]}in“”|*[!0-9]*)return 1;;esac;}使用bash的正则表达式isint_Regx(){[[$1=~^[+-]?[0-9]+$]];}

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

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

使用参数展开isnum_Parm(){local ck=${1#[+-]};ck=${ck/.};[“$ck”]&&[-z“${ck//[0-9]}”];}使用bash的正则表达式isnum_Regx(){[[$1=~^[+-]?([0-9]+([.][0-9]*)?|\.[0-9]])$]];}用例“”|中的isnum_Case(){Case${1#[-+]}|*[!0-9.]*|*.*.*)返回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[@]}";do
    outstr='';
    for func in isuint_{Parm,Grep,Bash,Case,Regx} isint_{Parm,Bash,Case,Regx} \
                isnum_{Parm,Case,Regx};do
        if $func "$var"; then
            outstr+='   ##'
        else
            outstr+='   --'
        fi
    done
    printf '%-11s %s\n' "$var" "$outstr"
done

应输出:

Value\Func   UPrm UGrp UBsh UCse URgx IPrm IBsh ICse IRgx NPrm NCse NRgx
0              ##   ##   ##   ##   ##   ##   ##   ##   ##   ##   ##   ##
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"
    done
    printf -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 line
    local -a order=()
    shift 3
    for func ;do
        testFunc "${ftyp}_$func" NaNTime "$tTest"
        testFunc "${ftyp}_$func" NumTime "$nTest"
        order[NaNTime+NumTime]=${ftyp}_$func\ $NumTime\ $NaNTime
    done
    printf '%-12s %11s %11s %14s\n' Function Number NaN Total
    min="${!order[*]}" min=${min%% *}
    for i in "${!order[@]}";do
        read -ra line <<<"${order[i]}"
        percent "$i" "$min" pct
        printf '%-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          Total
isuint_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          Total
isint_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          Total
isnum_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比较脚本,也可以在此处以文本形式下载完整的is num比较脚本。,(带UTF8和LATIN处理)。

结论

案件处理方式显然是最快的!大约比正则表达式快3倍,比使用参数扩展快2倍。在不需要时,应避免fork(到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 "$@";do
    printf "%-12s  " "${shell%% *}"
    $shell < <(testScript) 2>&1 | xargs
done

这表明:

bash          Success
dash          Success
busybox       Success
ksh           Success
zsh           Success

我知道其他基于bash的解决方案(如regex和bash的整数)在许多其他shell中都不起作用,并且fork资源昂贵,我更喜欢这种情况(就在参数扩展之前,这也是最兼容的)。

要捕获负数:

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

@charles Dufy和其他人已经给出了明确的答案。纯bash解决方案将使用以下内容:

string="-12,345"
if [[ "$string" =~ ^-?[0-9]+[.,]?[0-9]*$ ]]
then
    echo $string is a number
else
    echo $string is not a number
fi

尽管对于实数,在基数点之前不必有数字。

为了更全面地支持浮点数和科学记数法(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

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

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]浮点数中可以有两个+/-符号。我从奥卢的工作中学到了更多的规则,并测试了“”-“”-E-1“0-0”等坏字符串。下面是我的regex/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

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

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

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

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