Bluepostech

まじめなことを書きます

【bash】コマンドラインで四則演算をする

シェルスクリプトは十分有用な言語といえるが、ただ一つ重大な欠点がある。実は四則演算が単純な式でできないのだ。

たとえば、pythonで1/3を計算して表示するには、たった1文次のようにすればよい。

print 1.0/3.0

ただし1/3としてはならない。なぜならば1/3には整数型しかあらわれず、演算結果も整数の範囲でしか返ってこないからだ。つまり「print 1/3」では「0」と表示される。

このように同じスクリプト言語にもかかわらず、bashではこのようなことはできない。

$ echo 1.0/3.0
1.0/3.0

つまり、文字列「1.0/3.0」が返ってくる。

一応計算する方法はある。まず、ループでカウンタをインクリメントする、カウンタに1を足すような、整数の範囲で済む計算の場合、「$(( ))」でくくれば計算が実行される。

$ echo $((1+3))
4

ただし、これで計算できるのは整数型のみで、最初に出した1/3のようなものはうまくできない。

$ echo $((1/3))
0

しかも1.0/3とすると構文エラーとなりそもそも計算がまわらない。

小数も計算する方法はある。bcというコマンドとパイプ処理を使う方法だ。

bcコマンドは数式を受け取って計算を実行するコマンドだ。とはいえ引数に数式をとることができず、次のようにechoで出した数式をパイプで渡すか、別ファイルに数式を書いて読ませなければならない。

$ echo 'scale=3; 1.0/3' |bc
.333

これにも注意点があり、小数を扱う場合は小数点以下の桁数をscaleで指定してやる必要があり、それ以下は切り捨てられる。指定しなければ整数となり、小数点以下はまるまる切り捨てられる。

また、1の位が0の場合は省略され上記のように小数点から始まってしまう。スクリプト内で使う場合で表記をそろえる必要がある場合は、さらにパイプを連ねて出力を変換する必要がある。たとえばsedで処理する場合は次のようにする。

$ echo 'scale=3; 1.0/3' | bc | sed -e 's/^\./0\./g'
0.333

sedは文字列の置換、削除など多くの用法がある有用なコマンドだ。評価式を引数に与えて処理するのだが、's/hoge/huga/g'で「hogeをhugaに置き換える」、's/^huga/hoge/g'のように^を付けると「先頭のhugaのみをhogeに置き換える」という意味になる。また「.」は任意の1文字という意味になる特殊文字なので\でエスケープしている。なので上記コマンドで「先頭の『.』を『0.』と置き換える」ということを意味する。

一方、「パイプなんてよくわかんない可読性が悪い、見栄えが悪い」という場合は、何かしらのインタープリタにコマンドを直接与えるという方法がある。たとえばpythonの場合-cオプションでpythonのコマンドを直接実行できるので、桁数を指定しなければ次で計算結果は得られる。

$ python -c 'print 1.0/3.0'
0.333333333333

luaの場合は以下のようになる。

$ lua -e 'print(1.0/3.0)'
0.33333333333333

ただし言語によって切り捨てか四捨五入かが変わるので気をつけられたい。pythonluaは四捨五入のようだ。