Bashは算術演算ができますが、整数でしかできません。 浮動小数点の計算が必要な場合は、外部プログラム(awkやbcなど)を呼び出す必要があります。
算術演算子は、マニュアルのShell Arithmeticに(優先順位の順で)記載されています。
さらに、Bashが算術演算を行うのは特定の場所だけです(これらは算術の「コンテキスト」と呼ばれます)。
算術展開はコマンド置換に似ていますが、二重の括弧を使います。
$(( expression ))
この構文は、式を評価した結果に展開されます。 式の結果を変数に格納するためによく使われます。
x=$(( 3 + 4 ))
括弧の中に入れる空白の量は自由です。 スタイルと読みやすさの問題です。
x=$(( 3 + 4 ))
y=$((5+6))
この記事の筆者は、括弧の中で算術式がより見やすくなるように、空白を入れるのが好みです。
算術条件式は、先頭の$を_付けずに_、二重の括弧の中で実行します。
(( expression ))
これは結果に展開されません。 しかし、_終了ステータス_を生成します。
((の中では、BashはC言語と同じ真偽の考え方を使います。ゼロはfalse、ゼロ以外はtrueです。
これは、Bashが終了ステータスを評価する方法とは逆です!
この構文は、ふつう次の2つの場所で使います。
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]}"
# ................................^^^^^
配列についてもっと知りたい場合は、配列のコンセプトドキュメントを読んでください。
${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の用途は限られている、と筆者は考えています。
その主な利点は、「裸の」算術演算(算術式の構文を使わないもの)を可能にすることです。
これにより、コードを読む人が混乱する可能性があります。算術式が予期しない場所で評価されるからです。
算術コンテキストの、可読性を高める1つの側面を紹介します。
height=10
width=20
area=$(( height * width ))
算術式の中の変数から$が消えていることにお気づきになりましたか? Bashではそれができます。
式がずっと読みやすくなります。
マニュアルには次のように書かれています。
式の中では、シェル変数はパラメータ展開の構文を使わずに、名前で参照することもできます。
配列の要素でも同じことができます。
point1=(10 15) # these are (x, y) coordinates
point2=(20 7)
distance_squared=$(( (point1[0] - point2[0])**2 + (point1[1] - point2[1])**2 ))
ただし、その他のパラメータ式は専用の構文を使う必要があります。 たとえば、文字列の長さを2倍にする場合です。
s="hello world"
len=$(( ${#s} * 2 ))
位置パラメータも同様です。 最初の2つの位置パラメータが幅と高さを表しているなら、次のようになります。
area=$(( $1 * $2 ))