如何在 POSIX shell 脚本中检查变量是否为整数(避免周围有空格问题)?

如何在 POSIX shell 脚本中检查变量是否为整数(避免周围有空格问题)?

我的 POSIXis_integer ()函数很长一段时间都是这样的:

#!/bin/sh
is_integer ()
{
    [ "$1" -eq "$1" ] 2> /dev/null
}

然而今天我发现了破碎的。如果数字周围有一些空格,它的计算结果也会令人惊讶地为true,而且我不知道如何解决这个问题。


正确(预期)行为的示例:

is_integer 123评估为true.

不正确(意外)行为的示例:

is_integer ' 123'还评估为truefalse,但是它显然包含一个前导空格,因此在这种情况下该函数预计会计算为。


请仅提供符合 POSIX 标准的建议。谢谢。

答案1

#!/bin/sh
is_integer ()
{
    case "${1#[+-]}" in
        (*[!0123456789]*) return 1 ;;
        ('')              return 1 ;;
        (*)               return 0 ;;
    esac
}

仅使用 POSIX 内置函数。从规范中不清楚是否+1应该是整数,如果不是,则从行+中删除case

其工作原理如下。删除${1#[+-]}可选的前导符号。如果你留下的东西包含非数字,那么它就不是整数,同样,如果你什么都没有留下。如果它不是整数,那么它就是整数。

编辑:将 ^ 更改为 !否定字符类 - 感谢@LinuxSecurityFreak

答案2

不是最有效的(由于外部命令),但非常简单:

is_integer () {
  expr "X$1" : "X-\{0,1\}[0-9][0-9]*$" > /dev/null
}

至少在我正在测试的实现中,初始参数-不被视为匹配操作的一部分,而是显然被视为无效算术表达式的一部分;确保Xexpr其参数解析为有效的匹配操作。

答案3

更完整的解决方案如下:

is_integer() (
    export LC_ALL=C
    local n=${1#[-+]}
    case "$n" in
        0[0-7]*) case "$n" in 0*[!0-7]*)                 return 1;; esac;;
        0[xX]*)  case "$n" in 0[xX]|0[xX]*[!0-9a-fA-F]*) return 1;; esac;;
        *)       case "$n" in ''|*[!0-9]*)               return 1;; esac;;
    esac
)

这会去除任何前导符号,然后根据字符串是否具有0,0x或前缀来解析字符串0X。因此,应该注意在用作十进制数的值上不要有任意前导零。

$ echo $((01))
1
$ echo $((08))
-ash: arithmetic syntax error

相关内容