expr
恐らく最初に最初に覚える方法。
というか純粋なBourn Shell (sh)だと下のlet
や二重括弧記号が使えないのでこれが基本。
今やsh
もほとんどbash
へのリンクになったりしてるので
問題ないかもしれませんが、移植性を高くしたければexpr
にしておくのが無難。
$ a=1
$ a=`expr $a + 1`
$ echo $a
2
と言った感じ。
扱える数値は整数のみ。expr
に与える式は演算子の両側に必ずスペースが必要です。
expr
で四則演算などに文字列を渡すとエラーになります。
$ expr a + 1
expr: not a decimal number: 'a'
掛け算(*
)については、下についてもほとんどの場合共通ですが、
アスタリスク特殊文字なのでエスケープが必要です。
また、括弧を使った優先順位の指定も出来ますが、これもエスケープが必要。
$ expr \( 3 + 2 \) \* 4
20
余談ですが、bashなどだとこの様な評価結果を渡すのにはバックスラッシュ`
よりは
$ a=$(expr $a + 1)
と言った形が使えるので、こちらのが便利な事が多いです(見やすい、ネスト出来る、等) 1。
さらにexpr
は文字列を比較してマッチした数等を返す事も出来ます
2。
let
let
は文字列として与えられた式を評価して変数に入れます。
$ eq="1+1"
echo $eq
1+1
$ let a=$eq
$ echo $a
2
直接
$ a=1
$ let a=($a + 1)
$ echo $a
2
とわたしてもOK。let
で与える場合は"
でも(
で囲っても同じ結果です。
これらで囲まない場合はBashの代入規則通りスペース無しで書かないといけません。
また、letで評価される中身に関しては、文字が含まれると勝手に変数として 変換されるので、
$ let a=(a + 1)
としても同じ結果になります。
演算部を含めて文字列として与えてるので、四則演算を含めた変数をつくって
$ a="2+"
$ b="3"
$ let c=$a$b
$ echo $c
5
と言った感じに使うことも出来ます。
さらに、letでは変数をインクリメント演算子(++
)を使って直接インクリメントすることが出来ます。
$ let a=$a+1
と
$ let a++
は同じこと。
また、全ての変数を数値として宣言(declare -e
)しておくとlet
しなくても
計算する様になります。
$ declare -i a b c
$ a=1
$ b=2
$ c=$a+$b
$ echo "$a+$b=$c"
2+3=5
これも整数のみ。
二重括弧
$(())
とドル記号+二重括弧の中に式書く事で
letを使って渡すことと同じ事が出来ます。
$ a=1
$ b=2
$ c=$((a+b))
これも二重括弧の中はドル記号あっても無くても同じ。 この場合は直接
$ echo $((a+b))
みたいなことも出来るのでlet
よりも扱いやすいと思います。
分かりやすいので、今まではこれを使って
$ a=$((a+1))
みたいのを使っていました。
++
も使えるのですが、
$ $((++a))
bash: 2: command not found
と単純に書くと中身はインクリメントされるのですが、それをそのままコマンドとして 実行する様な形になってエラーになってしまうので。
$ a=$((++a))
とすればインクリメントされてきますが、
$ a=$((a++))
としてしまうと、右辺を$a
と評価した後右辺におけるa
をインクリメントして
右辺の評価値(すなわち元々の$a
)を再びa
に入れるので、結局一生そのままです。
なので文字数も一緒なので+1
しておいた方が分かりやすいかと。
コロン+二重括弧
シェルスクリプトを書いてる時に、if文等で何もしない行を組み込みたいときなど、
何も書かないとエラーになってしまいますが、:
を書くと、
何も実行しないがエラーにならなくなります。
if [ 1 -eq 1 ];then
# echo 処理をコメントアウトしたら中身が無くなる。
fi
のように、中身が無くなると
syntax error near unexpected token `fi'
と言ったエラーが出ます。これを避けるためには
if [ 1 -eq 1 ];then
# echo 処理をコメントアウトしたら中身が無くなる。
:
fi
の様に:
を加えて上げるとエラーを避けられます。
:
は何もせずにtrueを返すコマンドで、一応コマンドを実行してる事にはなるので、
エラーを避けられます。
:
の使い方としては処理のない所に取り敢えず置いとくもの、という認識だったんですが、
実は:
の右側にコマンドを書いておくことが出来て、
コマンドの内容が場合によっては実行されます。
これを使って、
$ : $((a++))
$ echo $a
2
と書くことが出来ます。
:
の右側に必ずスペースが必要です。
さらに短縮
と、色々見ているとさらに短縮できることが 3 !
How to use double or single bracket, parentheses, curly braces
$ a=1
$ ((a++))
$ echo $a
2
これだけで良い様です。
元々二重括弧内に式を書くとそれが評価され、
それを$
を前に付けることで中身の結果を取り出すような形なんだと思いますが、
これだけを実行してもOK、とのこと。
上のコロンに引き続き、この辺、きっちり何がどうなってるのか よく理解できて無いので適当に 別途書きなぐってみましたが、 取り敢えず、
$ ((a++))
がシェルスクリプトでの最も簡単なインクリメントの書き方だ、と言うことで。
小数を取り扱いたい時
上の方法は全てシェルの組み込みの物ですが、整数しか扱えません。
小数を扱いたい時はbc
や他のプログラムを使います。
bc
はそのまま起動するとインタラクティブに計算出来る様に立ち上がりますが、
式をパイプで渡してあげるとその式の結果を返してくれます。
但し、bc
ではscale
と言う変数で精度が定められていて、
初期値は0です。従って乗除するとき、そのままだと、
$ bc
scale
0
5/2
2
となります。 途中でscaleを変えれば
scale=2
5/2
2.50
の様に変更できますが、
scaleの初期値を簡単に変更するには-l
オプションを使うのが便利で、
これを使うとscaleが20になります。
-l
オプションは本来数学ライブラリを追加するオプションで、
これにより三角関数(s()等)や指数関数(e())を使える様になります。
bc
はbashrc等で
alias bc="bc -l"
とエイリアスしておくと便利です。
これを使って
$ a=$(echo 5 / 2 | bc)
$ echo $a
2.50000000000000000000
とすれば小数をシェルスクリプト内で扱えます。
これをこんな感じで
1 2 3 4 5 6 7 8 9 10 |
|
スクリプトにしておけば
$ calcBC.sh 5 / 2
2.50000000000000000000
の様にexpr
コマンド的に使えます。
最初にごにょごにょしてるのは
bc
では3*10^3
みたいな^
使った指数表記は使えますが、
e
を底とした浮遊小数点表記3e10
が使えないので、
上のスクリプトでは3e10
等を変換するためです。
他の言語のアウトプットでe
を使った表記がよくあるので
そのまま使えるようにするために。
**
も使えないので^
にしています。
勿論bc
でなくても計算出来るコマンドなら何でも良い訳で
perl
なんかを使って
こんなのを.bashrc
に書いておけば
calc
と言うコマンドでexpr
的に使えます。
bc
と違って逆に^
が使えないので逆に変換してます。
素直にperlのスクリプトを作っても良いわけですが、
簡単に.bashrc
に書いて関数にして使いたかったのでこんな感じに。
実は両者とも随分昔に随分面倒な事を数値や演算子を一つ一つ見て、 みたいなことをしたのを書いて使ってたんですが、 改めて見たら多分こんな感じで素直にやる方が良いのかと。
ということで、以上です 4 。
これをすると
expr
の移植性が高いのが優位だ、という点が消えますが。。。 ↩-
expr
は文字列の評価(:
:matchしているかどうか、等)も行えて$ expr "abcdefg" : "abc" 3
と、マッチした場合は文字数を出力。 但し先頭からマッチしてないと駄目で
$ expr "abcdefg" : "bcd" 0
と途中を見ると失敗します。
また、評価する側に
\(\)
を含むとその中身を抜き出せます。$ expr "abcdefg" : "..\(..\)..." cd
sed
だとか他のコマンドが強力なので敢えてexpr
でやることは無いと思いますが こういうのもある、と言うことで。 ↩ -
ちょっと余談でこのページを見つけるときに別の怪しげなサイトが。。。
怪しげなのに良く検索に引っかかってしまうサイト: プログラム問答 ja.softuses.com](/blog/2013/11/13/computer-bash-colon/)
-
最後の余談として、 最初に挙げたバッシュの画像なんですが、
Bash
という名前の物があったもので。 多分、一般名ではなくて商品名としてのもの? ↩