我只是不知道如何确保传递给脚本的参数是否为数字。
我只想这样做:
test *isnumber* $1 && VAR=$1 || echo "need a number"
有什么帮助吗?
我只是不知道如何确保传递给脚本的参数是否为数字。
我只想这样做:
test *isnumber* $1 && VAR=$1 || echo "need a number"
有什么帮助吗?
当前回答
Stack弹出了一条消息,问我是否真的想在回答30+后回答?但当然!!!使用bash新功能,如下所示:(在评论之后我做了更改)
函数isInt(){([[$1-eq$(($1+0))]]2>/dev/null&&[[$1!=“”]]&&echo 1)|| echo“”}
function isInt() {
([[ $1 =~ ^[-+0-9]+$ ]] && [[ $1 -eq $(( $1 + 0 )) ]] 2>/dev/null && [[ $1 != '' ]] && echo 1) || echo ''
}
支架:
===============out-of-the-box==================
1. negative integers (true & arithmetic),
2. positive integers (true & arithmetic),
3. with quotation (true & arithmetic),
4. without quotation (true & arithmetic),
5. all of the above with mixed signs(!!!) (true & arithmetic),
6. empty string (false & arithmetic),
7. no value (false & arithmetic),
8. alphanumeric (false & no arithmetic),
9. mixed only signs (false & no arithmetic),
================problematic====================
10. positive/negative floats with 1 decimal (true & NO arithmetic),
11. positive/negative floats with 2 or more decimals (FALSE & NO arithmetic).
只有当与[[$(isInt<arg>)]]中的过程替换结合使用时,才能从函数中获得真/假,因为bash中没有逻辑类型,也没有函数的返回值。
当测试表达式的结果为“错误”时,我使用大写,反之亦然!
通过“算术”,我的意思是bash可以像以下表达式那样进行数学运算:$x=$(($y+34))。
当在数学表达式中,参数的行为与预期一致时,我使用“算术/无算术”;当参数与预期行为相比表现不佳时,我则使用“无算术”。
正如你所看到的,只有10和11是有问题的!
完美的
PS:请注意,最流行的答案在情况9中失败!
其他回答
我找到了一个很短的版本:
function isnum()
{
return `echo "$1" | awk -F"\n" '{print ($0 != $0+0)}'`
}
在这里用一个正则表达式来测试整个部分和小数部分,并用点分隔。
re="^[0-9]*[.]{0,1}[0-9]*$"
if [[ $1 =~ $re ]]
then
echo "is numeric"
else
echo "Naahh, not numeric"
fi
一些性能和兼容性提示
对于不同类型的测试,有一些非常不同的方法。
我回顾了最相关的方法并进行了比较。
无符号整数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资源昂贵,我更喜欢这种情况(就在参数扩展之前,这也是最兼容的)。
接受的答案在所有情况下都不适用于我,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
[[ $1 =~ ^-?[0-9]+$ ]] && echo "number"
别忘了——包括负数!