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

Nic*_*oul 72 shell quoting arithmetic expr

expr 似乎不喜欢括号(在数学中用于显式运算符优先级):

expr 3 * (2 + 1)
bash: syntax error near unexpected token `('
Run Code Online (Sandbox Code Playgroud)

如何在bash中表达操作员优先级?

Ber*_*ard 86

您可以改用算术扩展。

echo "$(( 3 * ( 2 + 1 ) ))"
9
Run Code Online (Sandbox Code Playgroud)

在我个人看来,这看起来比使用expr.

man bash

算术扩展 算术扩展允许对算术表达式求值并替换结果。算术展开的格式为:

         $((expression))
Run Code Online (Sandbox Code Playgroud)

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

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


cuo*_*glm 48

使用let内置 bash 的另一种方法:

$ let a="3 * (2 + 1)"
$ printf '%s\n' "$a"
9
Run Code Online (Sandbox Code Playgroud)

笔记

正如@Stéphane Chazelas 所指出的那样bash您应该((...))用来进行算术运算exprlet为了易读性。

为了可移植性,使用$((...))@Bernhard answer

  • 我不是说这是错误的,我只是说它不应该被使用,因为有更好的选择(一种用于易读性 `((a = 3 * (2 + 1) ))`,一种用于可移植性 `a=$ ((3 * (2 + 1)))`),所以这不是反对你或你的答案的注释,而是反对它是选定的答案和得分最高的人。 (4认同)
  • 没有理由使用`let`。它并不比 `(( a = 3 * (2 + 1) ))`(两者都来自 `ksh` 并且仅在 ksh、bash 和 zsh 中可用)更标准或可移植,并且它不太清晰或易于引用。使用 `a=$((3 * (2 + 1)))` 是可移植的。 (3认同)
  • +1 更具可读性!我发布了我的问题+答案,只是认为这对我的 Linux 用户会有所帮助,但现在我从其他答案中受益匪浅 :-) (2认同)

Sté*_*las 42

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

POSIX 定义了$((...))扩展运算符。所以你可以在所有符合 POSIX 的 shell 中使用它(sh所有现代类 Unix 的shell ,dash、bash、yash、mksh、zsh、posh、ksh ......)。

a=$(( 3 * (2 + 1) ))
a=$((3*(2+1)))
Run Code Online (Sandbox Code Playgroud)

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

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

但是,由于引用使它变得笨拙且不太清晰(expr与当然的程度不同),因此ksh还引入了((...))另一种形式:

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

这更清晰,应该使用。

let((...))仅在kshzsh和 中可用bash。的$((...))语法应首选如果需要移植到其他外壳,expr只需要用于预POSIX类似Bourne壳(典型的是Bourne壳或壳Almquist的早期版本)。

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

  • 我每天都从你那里学到新东西,史蒂芬。我非常感谢您的 POSIX shell 知识! (2认同)

Gil*_*il' 18

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

expr 3 \* \( 2 + 1 \)
Run Code Online (Sandbox Code Playgroud)

除非您在 1970 年代或 1980 年代的老式 unix 系统上工作,否则几乎没有理由使用expr. 在过去,shell 没有执行算术的内置方法,您必须调用该expr实用程序。所有 POSIX shell 都通过算术扩展语法内置了算术

echo "$((3 * (2 + 1)))"
Run Code Online (Sandbox Code Playgroud)

该构造$((…))扩展为算术表达式的结果(以十进制表示)。与大多数 shell 一样,Bash 仅支持整数算术模 2 64(或模 2 32用于旧版本的 bash 和 32 位机器上的其他一些 shell)。

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

((x = 3 * (2+1)))
echo "$x"
if ((x > 3)); then …
Run Code Online (Sandbox Code Playgroud)

除了整数运算之外,expr还提供了一些字符串操作函数。这些也包含在 POSIX shell 的特性中,除了一个:expr STRING : REGEXP测试字符串是否与指定的正则表达式匹配。没有外部工具,POSIX shell 无法做到这一点,但 bash 可以[[ STRING =~ REGEXP ]](使用不同的正则表达式语法expr是经典工具并使用 BRE,bash 使用 ERE)。

除非您维护在 20 年前的系统上运行的脚本,否则您不需要知道它expr曾经存在过。使用 shell 算法。


Nic*_*oul 14

使用带引号的括号:

expr 3 '*' '(' 2 '+' 1 ')'
9
Run Code Online (Sandbox Code Playgroud)

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

  • Nicolas 说明但没有说明的是,`expr` 命令行上的标记必须用空格分隔;所以; 例如,`expr 3 "*" "(2" "+" "1)"` *** 将不起作用***。(另外,顺便说一句,您可能不需要引用`+`。) (3认同)