算术

算术 属于 Bash

{other: "%{count} 个练习"}

关于 算术

Bash 可以进行算术运算,但_只能处理整数_。 如果你需要进行浮点运算,就得调用外部程序(比如 awk 或 bc)。

算术运算符在手册中按优先级顺序列出:Shell Arithmetic。

此外,Bash 只在某些地方执行算术运算(称为算术“上下文”)。

算术展开

算术展开看起来像命令替换,只不过用的是双括号。

$(( expression ))

这种语法会展开为表达式求值的结果。 通常用它把结果保存到变量中:

x=$(( 3 + 4 ))
Note

括号里放多少空白由你决定。 这属于风格和可读性的问题。

x=$(( 3 + 4 ))
y=$((5+6))

和善的作者喜欢加上空格,这样括号里的算术表达式会更醒目。

算术条件

算术条件在双括号内执行,_不需要_开头的 $:

(( expression ))

它不会展开为结果,但会产生一个_退出状态_。

  • 如果表达式的结果为非零,退出状态就是 0(成功)。
  • 如果表达式的结果为零,退出状态就是 1(失败)。
Note

在 (( 中,Bash 对 true/false 的理解与 C 语言相同:零为假,非零为真。 这与 Bash 判断退出状态的方式正好相反!

这种语法通常用在两个地方:

  1. 作为 if 语句或 while 循环的 COMMAND 部分。

    if (( x == y - 1 )); then ...
    while (( y > 0 )); do ...
    
  2. 作为独立命令来修改变量的值。

    (( x = x + 1 ))
    echo "new value for x is $x"
    
    Caution

    这样使用算术运算再配合 set -e 时要小心。 如果表达式求值为零,退出状态就会是非零,Bash 会终止。

    x=0
    (( x++ ))
    echo "you will see this"
    
    set -e
    x=0
    (( x++ ))
    echo "shell exits on the previous line; you won't see this"
    

    关于 ++ 运算符更详细的讨论,参见这篇关于后置自增与前置自增的文章。

其他具有算术上下文的地方

还有一些其他地方也属于算术上下文:

  • 算术 for 循环。

    for (( i = 0; i < 10; i++ )); do
        echo "$i"
    done
    
  • 数值索引数组的下标部分。

    echo "The next element is ${array[x + 1]}"
    # ................................^^^^^
    

    想进一步了解数组,请阅读数组概念文档。

  • ${var:offset:length}展开中的 offset 和 length 部分。

  • let 内置命令。

    • 注意,推荐使用 (( expression )) 而不是 let "expresson"。 与算术条件结构不同,let 表达式会受到文件名展开和单词拆分的影响,而且需要用引号括起来。
  • 变量赋值,前提是该变量具有“整数属性”。

    # variable x does not have the integer attribute
    x="3 + 4"
    declare -p x      # => declare -- x="3 + 4"
    
    # variable y has the integer attribute
    declare -i y
    y="4 + 5"
    declare -p y      # => declare -i y="9"
    
    Note

    作者认为 declare -i 的用处有限。 它最大的好处是允许“裸”算术(不使用算术表达式语法)。 这可能会让读你代码的人感到困惑,因为算术表达式会在意想不到的地方被求值。

在算术表达式中使用变量

算术上下文有一个特点可以提升可读性。

height=10
width=20
area=$(( height * width ))

你有没有注意到,算术表达式中的变量少了 $ 美元符号? Bash 允许你这样做。 这让表达式容易读得多。

手册中说:

在表达式中,shell 变量也可以直接按名称引用,而不必使用参数展开语法。

它对数组元素也一样有效:

point1=(10 15)  # these are (x, y) coordinates
point2=(20 7)
distance_squared=$(( (point1[0] - point2[0])**2 + (point1[1] - point2[1])**2 ))

但其他参数表达式仍需要使用各自的特殊语法。 例如,把字符串的长度翻倍:

s="hello world"
len=$(( ${#s} * 2 ))

位置参数也是如此。 如果前两个位置参数分别表示宽度和高度:

area=$(( $1 * $2 ))
通过 GitHub 编辑 该链接会在新窗口或标签页中打开