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

我只想这样做:

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

有什么帮助吗?


当前回答

一些性能和兼容性提示

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

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

无符号整数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资源昂贵,我更喜欢这种情况(就在参数扩展之前,这也是最兼容的)。

其他回答

这可以通过使用grep查看所讨论的变量是否与扩展正则表达式匹配来实现。

测试整数1120:

yournumber=1120
if echo "$yournumber" | grep -qE '^[0-9]+$'; then
    echo "Valid number."
else
    echo "Error: not a number."
fi

输出:有效数字。

测试非整数1120a:

yournumber=1120a
if echo "$yournumber" | grep -qE '^[0-9]+$'; then
    echo "Valid number."
else
    echo "Error: not a number."
fi

输出:错误:不是数字。


解释

grep和-E开关允许我们使用扩展正则表达式“^[0-9]+$”。此正则表达式表示变量从^开始到$结束只应[]包含0-9 0到9的数字,并且至少应有+1个字符。grep,-q quiet开关关闭任何输出,无论它是否找到任何内容。if检查grep的退出状态。退出状态0表示成功,大于0表示错误。如果grep命令找到匹配项,则其退出状态为0,如果没有找到,则为1;

因此,在if测试中,我们将所有这些放在一起,返回变量$yournumber并|将其通过管道传递给grep,grep使用-q开关与-E扩展正则表达式“^[0-9]+$”表达式匹配。如果grep成功找到匹配项,则grep的退出状态为0,如果没有找到匹配项则为1。如果匹配成功,我们将返回“有效数字”。如果匹配失败,我们将回显“错误:不是数字”。


对于浮球或双打

我们可以将正则表达式从“^[0-9]+$”更改为“^[0-10]*\”。?[0-9]+$'表示浮点或双精度。

测试浮子1120.01:

yournumber=1120.01
if echo "$yournumber" | grep -qE '^[0-9]*\.?[0-9]+$'; then
    echo "Valid number."
else
    echo "Error: not a number."
fi

输出:有效数字。

测试浮子11.20.01:

yournumber=11.20.01
if echo "$yournumber" | grep -qE '^[0-9]*\.?[0-9]+$'; then
    echo "Valid number."
else
    echo "Error: not a number."
fi

输出:错误:不是数字。


对于否定词

要允许负整数,只需将正则表达式从“^[0-9]+$”更改为“^\-?[0-9]+$'.

要允许负浮点数或双精度,只需将正则表达式从“^[0-9]*\”更改。?[0-9]+$'到'^\-?[0-9]*\.?[0-9]+$'.

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

使用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]+$`

我使用以下(整数):

## ##### 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

接受的答案在所有情况下都不适用于我,BASH 4+因此:

# -- is var an integer? --
# trim leading/trailing whitespace, then check for digits return 0 or 1
# Globals: None
# Arguments: string
# Returns: boolean
# --
is_int() {
    str="$(echo -e "${1}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
    case ${str} in ''|*[!0-9]*) return 1 ;; esac
    return 0
}

如何使用它?

有效(将返回0=true):

is_int "100" && echo "return 0" || echo "return 1"

无效(将返回1=false):

is_int "100abc" && echo "returned 0" || echo "returned 1"
is_int ""  && echo "returned 0" || echo "returned 1"
is_int "100 100"  && echo "returned 0" || echo "returned 1"
is_int "      "  && echo "returned 0" || echo "returned 1"
is_int $NOT_SET_VAR  && echo "returned 0" || echo "returned 1"
is_int "3.14"   && echo "returned 0" || echo "returned 1"

输出:

returned 0
returned 1
returned 1
returned 1
returned 1
returned 1
returned 1

注意,在Bash中,1=假,0=真。我只是把它打印出来,而更可能是这样的:

if is_int ${total} ; then
    # perform some action 
fi

公认的答案在这里行不通,我在MacOS上。以下代码有效:

if [ $(echo "$number" | grep -c '^[0-9]\+$') = 0 ]; then 
    echo "it is a number"
else
    echo "not a number"
fi