我的 POSIXis_integer ()
函数很长一段时间都是这样的:
#!/bin/sh
is_integer ()
{
[ "$1" -eq "$1" ] 2> /dev/null
}
然而今天我发现了破碎的。如果数字周围有一些空格,它的计算结果也会令人惊讶地为true
,而且我不知道如何解决这个问题。
正确(预期)行为的示例:
is_integer 123
评估为true
.
不正确(意外)行为的示例:
is_integer ' 123'
还评估为true
false
,但是它显然包含一个前导空格,因此在这种情况下该函数预计会计算为。
请仅提供符合 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
}
至少在我正在测试的实现中,初始参数-
不被视为匹配操作的一部分,而是显然被视为无效算术表达式的一部分;确保X
将expr
其参数解析为有效的匹配操作。
答案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