我只是不知道如何确保传递给脚本的参数是否为数字。
我只想这样做:
test *isnumber* $1 && VAR=$1 || echo "need a number"
有什么帮助吗?
我只是不知道如何确保传递给脚本的参数是否为数字。
我只想这样做:
test *isnumber* $1 && VAR=$1 || echo "need a number"
有什么帮助吗?
当前回答
要捕获负数:
if [[ $1 == ?(-)+([0-9.]) ]]
then
echo number
else
echo not 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
printf '%b' "-123\nABC" | tr '[:space:]' '_' | grep -q '^-\?[[:digit:]]\+$' && echo "Integer." || echo "NOT integer."
删除-\?在grep匹配模式中,如果不接受负整数。
这是一个有点粗糙的边缘,但有点新手友好。
if [ $number -ge 0 ]
then
echo "Continue with code block"
else
echo "We matched 0 or $number is not a number"
fi
这将导致一个错误,如果$number不是一个数字,则会打印“非法数字:”,但它不会跳出脚本。奇怪的是,我找不到一个测试选项来测试整数。这里的逻辑将匹配任何大于或等于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资源昂贵,我更喜欢这种情况(就在参数扩展之前,这也是最兼容的)。
http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_04_03.html
您也可以使用bash的字符类。
if [[ $VAR = *[[:digit:]]* ]]; then
echo "$VAR is numeric"
else
echo "$VAR is not numeric"
fi
数字将包括空格、小数点和“e”或“e”表示浮点。
但是,如果指定C样式十六进制数,即“0xffff”或“0xffff”,则[[:digital:]]返回true。这里有点陷阱,bash允许你做一些类似“0xAZ00”的事情,但仍然将其算作一个数字(这不是GCC编译器的一些奇怪的怪癖,允许你对16以外的基使用0x符号吗??)
如果您的输入完全不可信,除非您想接受十六进制数字,否则您可能需要在测试“0x”或“0x”之前测试它是否为数字。这将通过以下方式实现:
if [[ ${VARIABLE:1:2} = "0x" ]] || [[ ${VARIABLE:1:2} = "0X" ]]; then echo "$VAR is not numeric"; fi