Bash 可以進行算術運算,但_只能處理整數_。 如果你需要做浮點數運算,就必須呼叫外部程式(例如 awk 或 bc)。
算術運算子都列在手冊裡(依優先順序排列):Shell Arithmetic。
此外,Bash 只會在特定地方進行算術運算(也就是所謂的算術「情境」)。
算術展開看起來像是 指令替換,只是改用雙重括號。
$(( expression ))
這個語法會展開成運算式求值後的結果。 它通常用來把結果存進變數:
x=$(( 3 + 4 ))
括號內要留多少空白,由你自己決定。 這關乎風格與可讀性。
x=$(( 3 + 4 ))
y=$((5+6))
你親切的作者喜歡加上空白,讓算術運算式在括號內更顯眼。
算術條件式是在雙重括號內執行,而且_不需要_開頭的 $:
(( expression ))
這不會展開成運算結果。 不過它會產生一個_結束狀態_。
在 (( 裡面,Bash 對 true/false 的認定和 C 語言一樣:零是 false,非零是 true。
這和 Bash 判斷結束狀態的方式正好相反!
這個語法通常用在兩個地方:
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"
想更深入了解 ++ 運算子,請看這篇關於 Postincrement vs Preincrement 的文章
還有其他幾個地方也屬於算術情境:
for (( i = 0; i < 10; i++ )); do
echo "$i"
done
數字索引陣列的索引部分。
echo "The next element is ${array[x + 1]}"
# ................................^^^^^
想進一步了解陣列,請閱讀 Arrays 概念文件。
${var: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 ))