Bash 可以进行算术运算,但_只能处理整数_。 如果你需要进行浮点运算,就得调用外部程序(比如 awk 或 bc)。
算术运算符在手册中按优先级顺序列出:Shell Arithmetic。
此外,Bash 只在某些地方执行算术运算(称为算术“上下文”)。
算术展开看起来像命令替换,只不过用的是双括号。
$(( expression ))
这种语法会展开为表达式求值的结果。 通常用它把结果保存到变量中:
x=$(( 3 + 4 ))
括号里放多少空白由你决定。 这属于风格和可读性的问题。
x=$(( 3 + 4 ))
y=$((5+6))
和善的作者喜欢加上空格,这样括号里的算术表达式会更醒目。
算术条件在双括号内执行,_不需要_开头的 $:
(( expression ))
它不会展开为结果,但会产生一个_退出状态_。
在 (( 中,Bash 对 true/false 的理解与 C 语言相同:零为假,非零为真。
这与 Bash 判断退出状态的方式正好相反!
这种语法通常用在两个地方:
作为 if 语句或 while 循环的 COMMAND 部分。
if (( x == y - 1 )); then ...
while (( y > 0 )); do ...
作为独立命令来修改变量的值。
(( x = x + 1 ))
echo "new value for x is $x"
这样使用算术运算再配合 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 (( i = 0; i < 10; i++ )); do
echo "$i"
done
数值索引数组的下标部分。
echo "The next element is ${array[x + 1]}"
# ................................^^^^^
想进一步了解数组,请阅读数组概念文档。
${var:offset:length}展开中的 offset 和 length 部分。
(( 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"
作者认为 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 ))