シェルの算術式展開の事例

以下、yahoo知恵袋の質問投稿より。

引数の整数値の合計を計算し、計算結果を出力して終了するシェルスクリプトの書き方を教えて下さい!

私は以下のスクリプトを回答した。

 1#!/bin/bash
 2i=1
 3max=$#
 4ret=0
 5while [ $i -le $max ]
 6do
 7  ret=`expr $ret + $1`
 8  i=`expr $i + 1`
 9  shift 1
10done
11echo $ret

これでも全然問題なく動作するが、その後、これをたった3行で実現する方法が回答された。

1#!/bin/bash
2a="$@"
3echo $((${a// /+}))

最初これをみた時、何をやっているか分からなかった。
調査を進めていくうちに、「算術式展開」というものだということがわかった。

「算術式展開」
→ $(( 算術式 )) のこと。コマンドなどの評価に先立って算術式評価が行われ、その結果で置換される。

上のシェルを詳細にみてみる。
$@は、シェル引数配列。これを変数aに代入している。 例えば、このシェルのファイル名をtes.shとし、コマンドラインより./tes.sh 1 2 3 4 5と入力して実行すると、変数aには1 2 3 4 5が格納される。
次に算術式${a// /+}は、変数aの空白を+に置換している。上の例だと、この算術式展開の結果は、1+2+3+4+5となる。
最後に$((1+2+3+4+5))が評価(計算)されて、15となる。

実際に確認した結果を載せる。

 1$ cat tes.sh
 2#!/bin/bash
 3a="$@"
 4echo $a
 5echo ${a// /+}
 6echo $((${a// /+}))
 7$ ./tes.sh 1 2 3 4 5
 81 2 3 4 5
 91+2+3+4+5
1015

参考

位置パラメータへのアクセス

変数意味
$0実行中のファイル名
$11番めの変数
$22番めの変数
$NN番めの変数(0-9)だけ
${N}N番めの変数、10番以降は必須
$@$1..$N までの引数配列すべて
$*$@と同じ。ただしIFS区切り
$#配列個数:つまり引数の個数
${@: X: n}x番目からN個のスライス

パラメータの展開

形式説明
${parameter:-word}
${parameter-word}
parameterのデフォルト値を指定する。
${parameter:=word}
${parameter=word}
parameterにデフォルト値を代入する。
${parameter:?word}
${parameter?word}
parameter未設定時にエラーメッセージを出して、シェルスクリプトを終了する。
${parameter:+word}
${parameter+word}
parameterが設定されている場合のみ、指定の値に展開する。
${#parameter}parameterの値の文字列の長さを求める。
${parameter#pattern}
${parameter##pattern}
parameterの値の文字列の左側から、一定のpatternを取り除く。
${parameter%pattern}
${parameter%%pattern}
parameterの値の文字列の右側から、一定のpatternを取り除く。
${parameter:offset}
${parameter:offset:length}
offsetやlengthを指定して、parameterの値の文字列を切り出す。
${parameter/pattern/string}
${parameter//pattern/string}
patternを指定して、parameterをstringで置き換える。//とすると全ての該当箇所を置換する。
${!変数名@}
${!変数名*}
指定した文字列で始まる変数名を一覧表示する。
${!parameter}parameterの値をparameter名とみなし、さらにその値を参照する。

※算術式展開は以下サイトが詳しい
Bash $((算術式)) のすべて - A 基本編

関連ページ