我有两根弦
str1="( 1 + 2 + 3 + 4 + 5 ) / 3 + 5"
和
str2="( 1 + 2 + 3 + 4 + 5 ) / 3 + 5 * 2"
命令
result=` expr $str1`
返回正确的值,同时
result=` expr $str2`
回报expr: syntax error
答案1
以下将产生错误的结果:
str2="( 1 + 2 + 3 + 4 + 5 ) / 3 + 5 * 2"
expr $str2
问题在于 shell 认为*
是通配符文件 glob,并将其替换为当前目录中的文件列表。这不是你想要的。
expr
是古老的。更现代的解决方案将使用 shell 的$((...))
形式进行算术:
$ str2="( 1 + 2 + 3 + 4 + 5 ) / 3 + 5 * 2"
$ echo "$((str2))"
15
shell 只进行整数运算。如果您确实想要浮点精确结果(此处恰好相同),请使用bc
:
$ echo "$str2" | bc -l
15.00000000000000000000
请注意,$str2
用双引号括起来,以防止 shell 恶作剧。
答案2
当您将变量扩展到双引号之外时(如 中所示)expr $str2
,会发生以下情况:
- 取变量的值。结果是一个字符串。
- 将值拆分为以空格分隔的块。结果是字符串列表。
- 将列表的每个元素解释为通配符模式,即通配。如果它与文件匹配,则用匹配列表替换该元素。如果它不匹配任何文件,则保留该元素。
例如,变量的值包含单词*
,该单词将替换为当前目录中的文件名列表。
一般来说,始终在变量替换和命令替换两边加上双引号:"$stuff"
, "`stuff`"
。看为什么我的 shell 脚本会因为空格或其他特殊字符而卡住?仅当您知道并理解为什么需要省略引号时才省略引号。
在这种情况下,您需要执行步骤 2(拆分),因为expr
需要将运算符和操作数放在单独的参数中。但步骤 3(将每个单词视为文件名通配符模式)一定不能发生。您可以通过关闭通配符来做到这一点:
result=`set -f; expr $str2`
在 ksh 或 bash 中,使用数组而不是字符串 — 但在这些 shell 中,您不会使用expr
.
除非您需要将脚本移植到古老的 Bourne shell,否则不需要使用expr
.算术表达式是标准 POSIX 功能。
result=$(($str2))
1分隔符可以通过IFS
变量进行配置。