expr算术中的括号:3 *(2 +1)


Answers:


40

使用letbash内置的另一种方法:

$ let a="3 * (2 + 1)"
$ printf '%s\n' "$a"
9

注意

正如@StéphaneChazelas所指出的那样bash您应该使用((...))来做算术运算exprlet使其易于辨认。

对于可移植性,请使用$((...))类似@Bernhard answer的方法


1
+1更可读!我发布了我的问题+答案,只是认为这对我的Linux用户会有所帮助,但是现在我从其他答案中受益匪浅:-)
Nicolas Raoul

3
没有理由使用let。它的标准性或可移植性都没有(( a = 3 * (2 + 1) ))(两者都来自ksh,并且仅在ksh,bash和zsh中可用),并且可读性或引用性较差。用于a=$((3 * (2 + 1)))便携式。
斯特凡Chazelas

2
我并不是说这是错误的,我只是说它不应该被使用,因为有更好的选择(一种代表易读性((a = 3 * (2 + 1) )),一种代表便携性a=$((3 * (2 + 1)))),因此它不是针对您或您的答案的注释,而是针对它作为选定的答案和得分最高的球员。
斯特凡Chazelas

@StéphaneChazelas:更新了我的答案!
cuonglm

我一直使用a=1 $[a+2]a=1 b=2 $[a+b]。他们有理由避免使用这种语法吗?
哥顿

73

您可以改用算术扩展。

echo "$(( 3 * ( 2 + 1 ) ))"
9

我个人认为,这比使用更好expr

man bash

算术扩展 算术扩展允许评估算术表达式和替换结果。算术扩展的格式为:

         $((expression))

将该表达式视为在双引号内,但括号内的双引号未得到特殊处理。表达式中的所有标记都经过参数扩展,字符串扩展,命令替换和引用删除。算术扩展可以嵌套。

根据以下“算术评估”下列出的规则进行评估。如果表达式无效,则bash会显示一条指示失败的消息,并且不会发生替换。


1
除了可读性之外,它也不需要分叉额外的过程来进行算术运算。它由外壳本身处理。
chepner 2014年

请注意,在POSIX Shell中,它会进行分词,因此在列表上下文中引用它是一个好习惯。
斯特凡Chazelas

当我在bash shell上尝试此操作时,我得到“非法变量名。”
lordhog

40

没有理由expr在现代shell中使用算术。

POSIX定义$((...))扩展运算符。因此,您可以在所有POSIX兼容外壳程序(sh所有现代Unix-like,dash,bash,yash,mksh,zsh,posh,ksh等)中使用它。

a=$(( 3 * (2 + 1) ))
a=$((3*(2+1)))

ksh还引入了一个let内建函数,该内函数传递了相同的算术表达式,它不会扩展为某种东西,而是根据表达式是否解析为0来返回退出状态,例如expr

if let 'a = 3 * (2 + 1)'; then
  echo "$a is non-zero"
fi

但是,由于报价不方便且不清晰(expr当然程度不同),因此ksh还引入了((...))另一种形式:

if (( a = 3 * (2 + 1) )) && (( 3 > 1 )); then
  echo "$a is non-zero and 3 > 1"
fi
((a+=2))

这更容易理解,应该改为使用。

let并且((...))是只适用于kshzshbash。的$((...))语法应首选如果需要移植到其他外壳,expr只需要换POSIX预类似Bourne壳(典型的是Bourne壳或壳Almquist的早期版本)。

在非伯恩前端,有一些带有内置算术运算符的shell:

  • csh/ tcsh(实际上是第一个内置有算术评估的Unix shell):

    @ a = 3 * (2 + 1)
  • akanga(基于rc

    a = $:'3 * (2 + 1)'
  • 作为历史记录,1989年在usenet上发布的Almquist shell的原始版本具有expr内置功能(实际上与合并test),但后来被删除了。


我每天都会从您那里学到新东西,斯特凡。我非常感谢您的POSIX Shell知识!
MattBianco 2014年

怎么: $((a = a*2))
Arthur2e5

如果我有浮点数怎么办?我的表达是a = $((-14 + 0.2 *(1 + 2 + 3)))。错误令牌为“ .2 *(1 + 2 + 3)”
布莱斯

@Blaise,那么您需要一个支持$((...))zsh,ksh93或yash之类浮点数的shell 。
StéphaneChazelas

16

expr是外部命令,不是特殊的shell语法。因此,如果要expr查看shell特殊字符,则需要通过引用它们来保护它们免受shell解析。此外,expr需要将每个数字和运算符作为单独的参数传递。从而:

expr 3 \* \( 2 + 1 \)

除非您正在研究1970或1980年代的老式Unix系统,否则几乎没有理由使用expr。在过去,shell没有内置的算法来执行算术,而您必须调用该expr实用程序。所有POSIX Shell都通过算术扩展语法具有内置算术

echo "$((3 * (2 + 1)))"

该构造$((…))扩展为算术表达式的结果(以十进制表示)。击,最喜欢的壳,仅支持整数运算模2 64(或模2 32对旧版本的bash和在32位机器一些其他shell)。

当您要执行赋值或测试表达式是否为0但不关心结果时,Bash提供了一种额外的便捷语法。此构造也存在于ksh和zsh中,但不存在于纯sh中。

((x = 3 * (2+1)))
echo "$x"
if ((x > 3)); then 

除整数算术外,expr还提供一些字符串操作函数。POSIX shell的功能也包含这些内容,但以下一项除外:expr STRING : REGEXP测试字符串是否与指定的regexp相匹配。没有外部工具,POSIX shell无法做到这一点,但是bash可以[[ STRING =~ REGEXP ]](使用不同的regexp语法 -这  expr是一个经典工具,并且使用BRE,bash使用ERE)。

除非您要维护在具有20年历史的系统上运行的脚本,否则您无需知道该脚本expr是否存在。使用壳算术。


expr foo : '\(.\)'也可以提取文字。bash的效果BASH_REMATCH类似。它还可以进行字符串比较,而POSIX [则不这样做(尽管可以想象使用sort它的方式)。
斯特凡Chazelas

下划线作为语法占位符---您是Schemer,@ Giles吗?:]
RubyTuesdayDONO

1
@RubyTuesdayDONO我在这里没有使用下划线。您是否误读了U + 2026水平省略号?如果是这样,请尝试使用更大的字体。
吉尔斯2015年

@Giles-好的,是的,由于我的字体大小,它看起来只像是一个下划线。对我来说,“ Schemer”是一个补充,它并不像省略号和下划线一样改变了含义……无需
对此大声疾呼

12

使用括号加上引号:

expr 3 '*' '(' 2 '+' 1 ')'
9

引号防止bash将括号解释为bash语法。


2
Nicolas说明但未解释的是,expr命令行上的标记必须用空格分隔;所以; 例如,expr 3 "*" "(2" "+" "1)" 将不起作用。(顺便说一句,顺便说一句,您可能不需要引用+。)
G-Man

括号是不一样的关键字while[[,他们的语法。如果它们是关键字,则它们在命令参数中不会被解释为关键字。您需要引号,以便bash不会解析引号,而是看到字符串文字。
吉尔斯2014年

1

如果您有。

echo '3 * (2 + 1)'|bc 
9                                                                    
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.