如何测试变量是否是Bash中的数字?

Flá*_*iro 543 linux bash shell

我无法弄清楚如何确保传递给我的脚本的参数是否为数字.

我想做的就是这样:

test *isnumber* $1 && VAR=$1 || echo "need a number"
Run Code Online (Sandbox Code Playgroud)

有帮助吗?

Cha*_*ffy 727

一种方法是使用正则表达式,如下所示:

re='^[0-9]+$'
if ! [[ $yournumber =~ $re ]] ; then
   echo "error: Not a number" >&2; exit 1
fi
Run Code Online (Sandbox Code Playgroud)

如果该值不一定是整数,请考虑适当修改正则表达式; 例如:

^[0-9]+([.][0-9]+)?$
Run Code Online (Sandbox Code Playgroud)

...或者,处理带有符号的数字:

^[+-]?[0-9]+([.][0-9]+)?$
Run Code Online (Sandbox Code Playgroud)

  • 我不确定为什么正则表达式必须保存在变量中,但如果是为了兼容性我不认为这是必要的.你可以直接应用表达式:`[[$ yournumber =〜^ [0-9] + $]]`. (20认同)
  • 我发现''exec>&2; 回声...''相当愚蠢.只是''回声......>&2'' (13认同)
  • 对于这种方法+1,但要注意小数,例如,使用"1.0"或"1,0"打印"错误:不是数字"进行此测试. (7认同)
  • @Ben你真的想要处理多个减号吗?我会把它变成'^ - ?`而不是`^ - *`,除非你真的正在做正确处理多次反演的工作. (4认同)
  • @SandraSchlichting使所有未来的输出转到stderr.这里并不是真正的重点,那里只有一个回声,但这是我倾向于在错误消息跨越多行的情况下进入的习惯. (4认同)
  • 我做了`if [[123 =〜'^ [0-9] + $']]; 然后回声好; 什么都没有.但是`re ='^ [0-9] + $'; 如果[[123 =〜$ re]]; 然后回声好; fi`说'好'.为什么?我是否必须在第一个版本中逃避某些事情? (4认同)
  • @FrozenFlame,将`= ~`的右侧放在引号中使它不再是正则表达式而是常规字符串(在现代版本的bash中,但不是一些古老的),而我在这个答案中给出的版本始终如一在每个版本中,支持`= ~`. (4认同)
  • @konsolebox是的,兼容性.在`= ~`右侧的文字正则表达式中的反斜杠处理在3.1和3.2之间变化,而赋值中的反斜杠处理在bash的所有相关版本中是恒定的.因此,遵循在使用`= ~`匹配它们之前始终为变量分配正则表达式的做法可以避免意外.我这里是为了教好习惯,即使这个特殊的正则表达式没有反斜杠逃脱. (3认同)
  • `exec>&2`做什么? (2认同)
  • @Charles Duffy One也可以考虑浮点数的形式.1234没有前导零和`^ [0-9]*([.] [0-9] +)?$`这需要额外检查一个非空弦强硬.. (2认同)

jil*_*les 259

没有bashisms(即使在System V sh中工作),

case $string in
    ''|*[!0-9]*) echo bad ;;
    *) echo good ;;
esac
Run Code Online (Sandbox Code Playgroud)

这会拒绝空字符串和包含非数字的字符串,接受其他所有内容.

负数或浮点数需要一些额外的工作.一个想法是排除-/ .在第一个"坏"模式中添加更多包含不恰当用途的"坏"模式(?*-*/ *.*.*)

  • +1 - 这是回归原始Bourne shell的惯用,可移植的方式,并且内置支持glob样式的通配符.如果你来自另一种编程语言,它看起来很怪异,但它比处理各种引用问题的脆弱性和无限的向后/侧向兼容性问题(如果测试......)更优雅. (19认同)
  • 您可以将第一行更改为`$ {string# - }`(在古老的Bourne shell中不起作用,但适用于任何POSIX shell)以接受负整数. (6认同)
  • 此外,这很容易扩展到浮点数 - 只需添加''.' |*.*.*`为不允许的模式,并在允许的字符上添加点.同样,你可以在之前允许一个可选的符号,虽然我更喜欢`case $ {string#[ - +]}`来简单地忽略符号. (4认同)
  • @Dor不需要引号,因为case命令无论如何都不会对该单词执行分词和路径名生成.(但是,大小写模式的扩展可能需要引用,因为它确定模式匹配字符是文字​​还是特殊字符.) (2认同)
  • 管道(竖线)字符的说明:https://unix.stackexchange.com/questions/85939/what-is-the-pipe-character-xy-in-the-case-statement (2认同)

Alb*_*gni 171

以下解决方案也可以在基本shell(如Bourne)中使用,而无需使用正则表达式.基本上任何使用非数字的数值评估操作都会导致错误,在shell中会将其隐式地视为false:

"$var" -eq "$var"
Run Code Online (Sandbox Code Playgroud)

如:

#!/bin/bash

var=a

if [ -n "$var" ] && [ "$var" -eq "$var" ] 2>/dev/null; then
  echo number
else
  echo not a number
fi
Run Code Online (Sandbox Code Playgroud)

你还可以测试$?更明确的操作的返回码:

[ -n "$var" ] && [ "$var" -eq "$var" ] 2>/dev/null
if [ $? -ne 0 ]; then
   echo $var is not number
fi
Run Code Online (Sandbox Code Playgroud)

标准错误的重定向是隐藏bash打印出来的"整数表达式预期"消息,以防我们没有数字.

CAVEATS(感谢下面的评论):

  • 带小数点的数字不会被识别为有效的"数字"
  • 使用[[ ]]而不是[ ]总是评估true
  • 大多数非Bash shell将始终将此表达式计算为 true
  • Bash中的行为没有记录,因此可能会在没有警告的情况下更改
  • 如果值包含数字后面的空格(例如"1 a")产生错误,例如 bash: [[: 1 a: syntax error in expression (error token is "a")
  • 如果该值与var-name相同(例如i ="i"),则产生错误,如 bash: [[: i: expression recursion level exceeded (error token is "i")

  • 小心使用单支架; `[[a -eq a]]`计算结果为true(两个参数都转换为零) (19认同)
  • 我仍然推荐这个(但引用的变量允许空字符串),因为结果保证*可用*作为Bash中的数字,无论如何. (8认同)
  • 我强烈建议不要使用这种方法,因为```builtin会将参数计算为算术的shell数量不是很少.ksh93和mksh都是如此.此外,由于这两个支持阵列都很容易进行代码注入.改为使用模式匹配. (5认同)
  • 非常好!请注意,这仅适用于整数,而不适用于任何数字.我需要检查一个必须是整数的参数,所以这很好用:`if![$#-eq 1 -o"$ 1"-eq"$ 1"] 2>/dev/null; then` (3认同)
  • @AlbertoZaccagni,在当前版本的bash中,这些值仅使用数字上下文规则来解释`[[]]`而不是`[]`.也就是说,这个行为没有通过`test`的POSIX标准和bash自己的文档来指定; 未来版本的bash可以修改行为以匹配ksh而不会破坏任何记录的行为承诺,因此依赖其当前持续存在的行为并不能保证是安全的. (3认同)

mru*_*cci 38

这测试一个数字是否为非负整数并且是独立于shell的(即没有bashisms)并且仅使用shell内置函数:

[ -z "${num##[0-9]*}" ] && echo "is a number" || echo "is not a number";
Run Code Online (Sandbox Code Playgroud)

但是错了.
正如jilles他的回答中评论和建议的那样,这是使用shell模式的正确方法.

[ ! -z "${num##*[!0-9]*}" ] && echo "is a number" || echo "is not a number";
Run Code Online (Sandbox Code Playgroud)

  • 这不能正常工作,它接受任何以数字开头的字符串.请注意$ {VAR ## WORD}中的WORD和类似的是shell模式,而不是正则表达式. (4认同)
  • `*[!0-9]*` 是一种匹配所有字符串至少有 1 个非数字字符的模式。`${num##*[!0-9]*}` 是一个“参数扩展”,我们获取 `num` 变量的内容并删除与模式匹配的最长字符串。如果参数扩展的结果不为空 (`! [ -z ${...} ]`) 那么它是一个数字,因为它不包含任何非数字字符。 (3认同)
  • 你能把这个表达翻译成英文吗?我真的很想使用它,但即使仔细阅读了 bash 手册页,我也不太了解它,无法信任它。 (2认同)

gle*_*man 37

没有人建议使用bash的扩展模式匹配:

[[ $1 == ?(-)+([0-9]) ]] && echo "$1 is an integer"
Run Code Online (Sandbox Code Playgroud)

  • Glenn,我从你的帖子中删除了'shopt -s extglob`(我赞成,这是我最喜欢的答案之一),因为在[Conditional Constructs]中(http://www.gnu.org/software/bash/manual/) bashref.html #Conditional-Constructs)你可以读:_当使用`==`和`!=`运算符时,运算符右边的字符串被认为是一个模式,并根据下面[Pattern]中描述的规则进行匹配匹配](http://www.gnu.org/software/bash/manual/bashref.html#Pattern-Matching),好像`extglob` shell选项已启用.我希望你不介意! (5认同)
  • @Jdamian:是的,它是在Bash 4.1中添加的(2009年底发布了…Bash 3.2在2006年发布了……现在是一个古老的软件,对那些过去受困的人感到抱歉)。此外,您可能会争辩到extglobs是在2.02版(1998年发布)中引入的,并且在<2.02版中不起作用…现在,您的评论将作为对较旧版本的警告。 (2认同)

pix*_*eat 26

我对直接解析shell中数字格式的解决方案感到惊讶.shell不太适合这种情况,是用于控制文件和进程的DSL.有足够数量的解析器稍微低一点,例如:

isdecimal() {
  # filter octal/hex/ord()
  num=$(printf '%s' "$1" | sed "s/^0*\([1-9]\)/\1/; s/'/^/")

  test "$num" && printf '%f' "$num" >/dev/null 2>&1
}
Run Code Online (Sandbox Code Playgroud)

将'%f'更改为您需要的任何特定格式.

  • 有趣的是,你试图通过复制其他语言的一些习语来变得聪明.不幸的是,这在shell中不起作用.贝壳非常特别,如果没有对它们的扎实了解,你很可能会写出破碎的代码.你的代码坏了:`isnumber''a"`将返回true.这在[POSIX规范](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html)中有记载,您将在其中阅读:_如果前导字符是单引号或双引号,该值应为单引号或双引号后字符的基础代码集中的数值._ (7认同)
  • `isnumber(){printf'%f'"$ 1"&>/dev/null && echo"这是一个数字"|| 回声"不是数字"; }` (4认同)
  • @sputnick你的版本打破了原始函数的固有(和有用)返回值语义.所以,只需将函数保持原样,并使用它:`isnumber 23 && echo"这是一个数字"|| 回声"不是数字"` (4认同)
  • 这不应该也有`2>/dev/null`,所以`isnumber"foo"`不会污染stderr? (4认同)
  • 调用像bash这样的现代shell"用于控制文件和进程的DSL"忽略了它们的用途远不止于此 - 一些发行版在其上构建了整个包管理器和Web界面(尽管可能很难看).批处理文件适合您的描述,因为即使设置变量也很困难. (4认同)
  • 它失败的另一种情况:`isinteger(){[[$ 1]] && printf'%d'"$ 1">/dev/null 2>&1; ``将失败,例如,'isinteger 09`:那是因为一个愚蠢的领先的'0`.现在你可能会争辩说`09`不应该被验证......无论如何,`isnumber''a"`已经很好地证明了所有这些设计都被打破了. (2认同)
  • 好多了!但它仍然验证包含尾随换行符的变量:`$'1 \n \n \n'`; 这是因为`$(...)`修剪尾随换行符.随你.你正批评那些明确解析字符串的方法(所以我们希望你的方法真的更优)但是:你的方法产生了3个子shell,甚至不是100%安全(关于追踪换行),不知怎的解析带有`sed`的字符串比直接解析字符串的方法效率低得多.做得好. (2认同)
  • “下面有足够的数字解析器”——但你没有使用一个,而是实现你自己的基于正则表达式的解决方案。由于 bash 具有本机正则表达式,这到底购买了什么? (2认同)

F. *_*uri 17

(2) 完全重写此答案:2021 年 6 月 27 日。

一些性能和兼容性提示

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

我回顾了最相关的方法并建立了这个比较。

无符号整数 is_uint()

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

有符号整数 is_int()

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

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

这些函数实现代码来评估表达式是否为浮点数,即如上但允许可选的小数点和后面的附加数字。这并不试图涵盖科学记数法中的数字表达式(例如 1.0234E-12)。

概念测试

(您可以在先前声明的函数之后复制/粘贴此测试代码。)

testcases=(
    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' Function \
       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+='  num'
        else outstr+='  str'
        fi
    done
    printf '%-11s %s\n' "|$var|" "$outstr"
done
Run Code Online (Sandbox Code Playgroud)

应该输出:

Function     UPrm UGrp UBsh UCse URgx IPrm IBsh ICse IRgx NPrm NCse NRgx
|1|           num  num  num  num  num  num  num  num  num  num  num  num
|42|          num  num  num  num  num  num  num  num  num  num  num  num
|-3|          str  str  str  str  str  num  num  num  num  num  num  num
|+42|         str  str  num  str  str  num  num  num  num  num  num  num
|+3.|         str  str  str  str  str  str  str  str  str  num  num  num
|.9|          str  str  str  str  str  str  str  str  str  num  num  num
|3.14|        str  str  str  str  str  str  str  str  str  num  num  num
|+3.141|      str  str  str  str  str  str  str  str  str  num  num  num
|-31.4|       str  str  str  str  str  str  str  str  str  num  num  num
||            str  str  num  str  str  str  str  str  str  str  str  str
|.|           str  str  str  str  str  str  str  str  str  str  str  str
|3-3|         str  str  num  str  str  str  str  str  str  str  str  str
|3.1.4|       str  str  str  str  str  str  str  str  str  str  str  str
|3a|          str  str  str  str  str  str  str  str  str  str  str  str
|a3|          str  str  str  str  str  str  str  str  str  str  str  str
|blah|        str  str  str  str  str  str  str  str  str  str  str  str
|Good day!|   str  str  str  str  str  str  str  str  str  str  str  str
Run Code Online (Sandbox Code Playgroud)

我希望!(注意: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
}
Run Code Online (Sandbox Code Playgroud)

我可以这样跑:

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
Run Code Online (Sandbox Code Playgroud)

在我的主机上,这显示:

Function          Number         NaN          Total
isuint_Case       6762µs      8492µs         15254µs    100.00%
isuint_Bash      13478µs     12739µs         26217µs    171.87%
isuint_Parm      11324µs     18807µs         30131µs    197.53%
isuint_Regx      20777µs     27616µs         48393µs    317.25%
isuint_Grep    1516390µs   1491751µs       3008141µs  19720.34%
Function          Number         NaN          Total
isint_Case        8630µs      8042µs         16672µs    100.00%
isint_Bash       14254µs     12272µs         26526µs    159.10%
isint_Parm       16445µs     20491µs         36936µs    221.54%
isint_Regx       23661µs     28287µs         51948µs    311.59%
Function          Number         NaN          Total
isnum_Case        9579µs     10328µs         19907µs    100.00%
isnum_Parm       21115µs     28983µs         50098µs    251.66%
isnum_Regx       35552µs     58453µs         94005µs    472.22%
Run Code Online (Sandbox Code Playgroud)

结论

  • case方法明明是最快的!regex比使用参数扩展快3 倍,比使用参数扩展快 2倍 。
  • grep在不需要时应避免分叉(to或任何二进制文件)。

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 ;}
Run Code Online (Sandbox Code Playgroud)

关于兼容性

为此,我根据以前的测试编写了一个小测试脚本,其中:

for shell in bash dash 'busybox sh' ksh zsh "$@";do
    printf "%-12s  " "${shell%% *}"
    $shell < <(testScript) 2>&1 | xargs
done
Run Code Online (Sandbox Code Playgroud)

由此可见:

bash          Success
dash          Success
busybox       Success
ksh           Success
zsh           Success
Run Code Online (Sandbox Code Playgroud)

据我所知,其他基于的解决方案,如regex的整数在许多其他 shell 中不起作用,而且fork资源昂贵,我更喜欢这种case方式(就在参数扩展之前,它也大多兼容)。


Ser*_*reu 15

我正在寻找答案,并意识到没有人想到FLOAT数字(带点)!

使用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
Run Code Online (Sandbox Code Playgroud)

在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
Run Code Online (Sandbox Code Playgroud)

要匹配JUST整数,请使用:

# change check line to:
check=`echo "$1" | grep -E ^\-?[0-9]+$`
Run Code Online (Sandbox Code Playgroud)


tri*_*e_r 11

只是跟进@mary.但是因为我没有足够的代表,所以不能将其作为对该帖子的评论发布.无论如何,这是我用过的:

isnum() { awk -v a="$1" 'BEGIN {print (a == a + 0)}'; }
Run Code Online (Sandbox Code Playgroud)

如果参数是数字,函数将返回"1",否则返回"0".这适用于整数和浮点数.用法类似于:

n=-2.05e+07
res=`isnum "$n"`
if [ "$res" == "1" ]; then
     echo "$n is a number"
else
     echo "$n is not a number"
fi
Run Code Online (Sandbox Code Playgroud)

  • 打印数字不如设置退出代码有用.''BEGIN {exit(1-(a == a + 0))}'`有点难以理解,但可以在函数中使用,它返回true或false,就像`````grep -q`等. (3认同)

Hac*_*chi 11

对于我的问题,我只需要确保用户不会意外输入一些文本,因此我试图保持简单易读

isNumber() {
    (( $1 )) 2>/dev/null
}
Run Code Online (Sandbox Code Playgroud)

根据手册页,这几乎可以满足我的要求

如果表达式的值非零,则返回状态为 0

为了防止“可能是数字”的字符串出现令人讨厌的错误消息,我忽略了错误输出

$ (( 2s ))
bash: ((: 2s: value too great for base (error token is "2s")
Run Code Online (Sandbox Code Playgroud)

  • 这是错误的(错误)!试试这个: `foo=1;set -- foo;(( $1 )) 2&gt;/dev/null &amp;&amp; echo "'$1' 是一个数字"` (2认同)

Jos*_*hih 10

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

测试整数1120

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

输出: Valid number.

测试非整数1120a

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

输出: Error: not a number.


解释

  • grep,该-E开关允许我们使用扩展正则表达式'^[0-9]+$'。这个正则表达式意味着变量应该只[]包含0-9从变量的^开头到$结尾的零到九的数字,并且应该至少有+一个字符。
  • grep,在-q安静的开关关闭任何输出是否发现任何东西。
  • if检查 的退出状态grep。退出状态0意味着成功,任何更大的都意味着错误。该grep命令有一个退出状态,即0是否找到匹配项,1何时未找到;

所以把它们放在一起,在if测试中,我们echo将变量$yournumber|管道传递给grep它,用-qswitch 悄悄地匹配-E扩展的正则表达式'^[0-9]+$'表达式。的退出状态grep将是0是否grep成功找到匹配项,1如果没有。如果匹配成功,我们echo "Valid number.". 如果匹配失败,我们echo "Error: not a number.".


对于浮动或双打

我们可以将正则表达式从'^[0-9]+$'更改'^[0-9]*\.?[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
Run Code Online (Sandbox Code Playgroud)

输出: Valid number.

测试浮点数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
Run Code Online (Sandbox Code Playgroud)

输出: Error: not a number.


对于阴性

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

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


Sam*_*tch 7

老问题,但我只想解决我的解决方案.这个不需要任何奇怪的shell技巧,或者依赖于永远不会存在的东西.

if [ -n "$(printf '%s\n' "$var" | sed 's/[0-9]//g')" ]; then
    echo 'is not numeric'
else
    echo 'is numeric'
fi
Run Code Online (Sandbox Code Playgroud)

基本上它只是从输入中删除所有数字,如果你留下一个非零长度的字符串,那么它不是一个数字.


use*_*246 7

test -z "${i//[0-9]}" && echo digits || echo no no no
Run Code Online (Sandbox Code Playgroud)

${i//[0-9]}$i空字符串替换值中的任何数字,请参阅man -P 'less +/parameter\/' bash.-z检查结果字符串的长度是否为零.

如果您还希望在$i空时排除大小写,则可以使用以下结构之一:

test -n "$i" && test -z "${i//[0-9]}" && echo digits || echo not a number
[[ -n "$i" && -z "${i//[0-9]}" ]] && echo digits || echo not a number
Run Code Online (Sandbox Code Playgroud)


小智 6

[[ $1 =~ ^-?[0-9]+$ ]] && echo "number"
Run Code Online (Sandbox Code Playgroud)

不要忘记-包含负数!


Dav*_* W. 6

我使用expr。如果您尝试向非数字值添加零,它将返回非零值:

if expr -- "$number" + 0 > /dev/null 2>&1
then
    echo "$number is a number"
else
    echo "$number isn't a number"
fi
Run Code Online (Sandbox Code Playgroud)

如果您需要非整数,可能可以使用bc,但我不相信bc有完全相同的行为。将零添加到非数字会使您为零,并且它也会返回零值。也许你可以结合bcexpr。用于bc向 中添加零$number。如果答案是0,则尝试expr验证它$number不为零。

  • 这是比较糟糕的。为了让它稍微好一点,你应该使用 `expr -- "$number" + 0`; 但这仍然会假装“0不是数字”。来自“man expr”:“如果 EXPRESSION 既不为 null 也不为 0,则退出状态为 0;如果 EXPRESSION 为 null 或 0,则退出状态为 1”, (2认同)

小智 6

@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
Run Code Online (Sandbox Code Playgroud)

尽管对于实数,在小数点之前没有数字是强制性的。

为了提供对浮点数和科学计数法的更彻底的支持(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
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

注意:我们可以列出十进制和科学计数法的句法要求,其中之一是允许逗号作为小数点以及“。”。然后,我们断言必须只有一个这样的小数点。[Ee]浮点数中可以有两个+/-符号。我从Aulu的工作中学到了一些规则,并针对诸如''-''-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
Run Code Online (Sandbox Code Playgroud)


kar*_*ttu 6

无法发表评论所以我会添加自己的答案,这是使用bash模式匹配的glenn jackman答案的扩展.

我最初需要的是识别数字并区分整数和浮点数.功能定义扣除为:

function isInteger() {
    [[ ${1} == ?(-)+([0-9]) ]]
}

function isFloat() {
    [[ ${1} == ?(-)@(+([0-9]).*([0-9])|*([0-9]).+([0-9]))?(E?(-|+)+([0-9])) ]]
}
Run Code Online (Sandbox Code Playgroud)

我使用单元测试(使用shUnit2)来验证我的模式是否按预期工作:

oneTimeSetUp() {
    int_values="0 123 -0 -123"
    float_values="0.0 0. .0 -0.0 -0. -.0 \
        123.456 123. .456 -123.456 -123. -.456
        123.456E08 123.E08 .456E08 -123.456E08 -123.E08 -.456E08 \
        123.456E+08 123.E+08 .456E+08 -123.456E+08 -123.E+08 -.456E+08 \
        123.456E-08 123.E-08 .456E-08 -123.456E-08 -123.E-08 -.456E-08"
}

testIsIntegerIsFloat() {
    local value
    for value in ${int_values}
    do
        assertTrue "${value} should be tested as integer" "isInteger ${value}"
        assertFalse "${value} should not be tested as float" "isFloat ${value}"
    done

    for value in ${float_values}
    do
        assertTrue "${value} should be tested as float" "isFloat ${value}"
        assertFalse "${value} should not be tested as integer" "isInteger ${value}"
    done

}
Run Code Online (Sandbox Code Playgroud)

注意:可以修改isFloat模式以更加容忍小数点(@(.,))和E符号(@(Ee)).我的单元测试只测试整数或浮点值,但不测试任何无效输入.


And*_*ndy 6

一种简单的方法是检查它是否包含非数字字符。您将所有数字字符替换为空并检查长度。如果有长度,它就不是数字。

if [[ ! -n ${input//[0-9]/} ]]; then
    echo "Input Is A Number"
fi
Run Code Online (Sandbox Code Playgroud)

  • 处理负数需要更复杂的方法。 (2认同)

小智 5

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
Run Code Online (Sandbox Code Playgroud)

数字将包括空格,小数点和浮点数的"e"或"E".

但是,如果指定C样式的十六进制数,即"0xffff"或"0XFFFF",[[:digit:]]将返回true.这里有一点陷阱,bash允许你做类似"0xAZ00"的东西并仍然把它算作一个数字(这不是来自GCC编译器的一些奇怪的怪癖,让你使用0x符号除16之外的基数??? )

如果您的输入完全不受信任,您可能希望在测试之前测试"0x"或"0X"是否为数字,除非您想接受十六进制数字.这将通过以下方式实现:

if [[ ${VARIABLE:1:2} = "0x" ]] || [[ ${VARIABLE:1:2} = "0X" ]]; then echo "$VAR is not numeric"; fi
Run Code Online (Sandbox Code Playgroud)

  • `[[$ VAR =*[[:digit:]]*]]`如果变量**包含**一个数字,则返回true,而不是****是一个整数. (15认同)

小智 5

我会试试这个:

printf "%g" "$var" &> /dev/null
if [[ $? == 0 ]] ; then
    echo "$var is a number."
else
    echo "$var is not a number."
fi
Run Code Online (Sandbox Code Playgroud)

注意:这将nan和inf识别为数字.

  • 而不是检查以前的状态代码,为什么不把它放在`if`本身?这就是`if`的作用......`如果printf"%g""$ var"&>/dev/null; 然后......` (3认同)
  • 或者像pixelbeat的回答一样重复,或者更适合作为评论(使用`%f`可能更好) (2认同)
  • 这有其他警告.它将验证空字符串,以及像'a`这样的字符串. (2认同)

3ro*_*nco 5

因为我最近不得不篡改这个,并且最喜欢karttu的单元测试方法。我修改了代码并添加了一些其他解决方案,自己尝试看看结果:

#!/bin/bash

    # N={0,1,2,3,...} by syntaxerror
function isNaturalNumber()
{
 [[ ${1} =~ ^[0-9]+$ ]]
}
    # Z={...,-2,-1,0,1,2,...} by karttu
function isInteger() 
{
 [[ ${1} == ?(-)+([0-9]) ]]
}
    # Q={...,-½,-¼,0.0,¼,½,...} by karttu
function isFloat() 
{
 [[ ${1} == ?(-)@(+([0-9]).*([0-9])|*([0-9]).+([0-9]))?(E?(-|+)+([0-9])) ]]
}
    # R={...,-1,-½,-¼,0.E+n,¼,½,1,...}
function isNumber()
{
 isNaturalNumber $1 || isInteger $1 || isFloat $1
}

bools=("TRUE" "FALSE")
int_values="0 123 -0 -123"
float_values="0.0 0. .0 -0.0 -0. -.0 \
    123.456 123. .456 -123.456 -123. -.456 \
    123.456E08 123.E08 .456E08 -123.456E08 -123.E08 -.456E08 \
    123.456E+08 123.E+08 .456E+08 -123.456E+08 -123.E+08 -.456E+08 \
    123.456E-08 123.E-08 .456E-08 -123.456E-08 -123.E-08 -.456E-08"
false_values="blah meh mooh blah5 67mooh a123bc"

for value in ${int_values} ${float_values} ${false_values}
do
    printf "  %5s=%-30s" $(isNaturalNumber $value) ${bools[$?]} $(printf "isNaturalNumber(%s)" $value)
    printf "%5s=%-24s" $(isInteger $value) ${bools[$?]} $(printf "isInteger(%s)" $value)
    printf "%5s=%-24s" $(isFloat $value) ${bools[$?]} $(printf "isFloat(%s)" $value)
    printf "%5s=%-24s\n" $(isNumber $value) ${bools[$?]} $(printf "isNumber(%s)" $value)
done
Run Code Online (Sandbox Code Playgroud)

所以isNumber()包括破折号、逗号和指数符号,因此在整数和浮点数上返回 TRUE,另一方面isFloat()对整数值返回 FALSE,isInteger()同样在浮点数上返回 FALSE。为了您的方便,所有作为一个班轮:

isNaturalNumber() { [[ ${1} =~ ^[0-9]+$ ]]; }
isInteger() { [[ ${1} == ?(-)+([0-9]) ]]; }
isFloat() { [[ ${1} == ?(-)@(+([0-9]).*([0-9])|*([0-9]).+([0-9]))?(E?(-|+)+([0-9])) ]]; }
isNumber() { isNaturalNumber $1 || isInteger $1 || isFloat $1; }
Run Code Online (Sandbox Code Playgroud)

  • isNumber 将在任何包含数字的字符串上返回 'true'。 (3认同)