四則演算
AWKでは一般的な四則演算 (加減乗除) を行うことができます。数値は全て倍精度浮動小数点数として扱われます。また、演算順序も学校で習うように、足し算と引き算よりも、掛け算と割り算が優先されます。それでは「1 + 2 - 3 * 4 / 5」を計算してみましょう。
$ echo '1 2 3 4 5' | \ > awk '{print $1 + $2 - $3 * $4 / $5}' 0.6
このようにシェル上で、与えられた数式を評価して計算を行う場合、bcコマンドなどを用いるのが一般的だと思います。しかし、bcコマンドには癖があり、初心者には扱いにくいかもしれません。
$ echo '1 + 2 - 3 * 4 / 5' | bc 1
また、bcコマンドは標準だと整数しか返しません。小数を扱いたい場合には、次のように"-l"オプションを使う必要があります。
$ echo '1 + 2 - 3 * 4 / 5' | bc -l .60000000000000000000
一方、AWKにはevalのような評価関数がないため、与えられた数式を簡単にパースすることができません注1。
注1:このように演算子(+、-、*、/)の前後に数値が配置された、小学校で習う数式の記述方法を「中置記法」といいます。中置記法をパースするのは簡単ではありませんが、書籍『プログラミング言語AWK』(USP研究所)では、その方法が詳しく解説されています。また、中置記法だけでなく、逆ポーランド記法(後置記法)のパース方法についても詳細な説明があります。
同書ではUnixを作り上げた天才たちが、AWKだけでなく他のプログラミングにおいても応用可能な基礎を解説しています。同書が「経典」と呼ばれている所以はこうしたところにあるのでしょう。
とはいえ、数式を与えて、計算結果を出力したい場合もあるでしょう。そこで筆者は、次のような関数をbashの関数として~/.bashrcに定義してあります。
calc() { awk "BEGIN {print $*}" }
とても小さな関数ですが、これで、コマンドライン上で簡単に数式を計算できるようになります。中身がAWKそのものなので、後述するAWKの組込関数も扱うことができます。
$ calc '1 + 2 - 3 * 4 / 5' 0.6
シェルが先に演算子を解釈してしまうため、数式はシングルクォートまたはダブルクォートで囲まないといけない点に注意してください。そうしないと「*」(アスタリスク)などが展開されてしまったり、「/」がルートディレクトリとして認識されたりして、正しい計算ができません。
三角関数
AWKには三角関数もあります。ところが、正弦を表すsin()関数、余弦を表すcos()関数があるだけで、正接を表すtan()関数がありません。これはtanがsin()関数とcos()関数だけで記述できるためです。学校で学んだように、tanはsinをcosで割ったものですから、tan(1)を求めるには次のようにすればよいのです。
$ awk 'BEGIN {print sin(1) / cos(1)}' 1.55741
もし、先ほど紹介したbashのcalc関数を導入しているのであれば、AWKの組込関数も使えますので、tanは次のようにして簡単に求まります。
$ calc 'sin(1) / cos(1)' 1.55741
さらに、このsin()関数とcos()関数の引数である角度の単位は「度」ではなく「ラジアン」です。学校での授業を思い出してほしいのですが、度からラジアンに変換するには、角度に対し、(パイ)を180度で割ったものを掛けます。90度をラジアンに変換してみましょう。
$ awk 'BEGIN {print 90 * 3.14 / 180}' 1.57
このように書くと、「は3.14」と決め打ちになっていることに違和感がある方がいらっしゃるでしょう。AWKでは を直接計算することができます。それがatan2()関数です。
$ awk 'BEGIN {print atan2(0, -0)}' 3.14159
atan2()関数の基本的な用途は、角度を求めるため、2つの引数で逆正接arctanを計算するというものです。とても癖があるのですが、atan2(0, -0)は であるという定義を使うことで、90度のラジアンは次のようにして求めることができます。
$ awk 'BEGIN {print 90 * atan2(0, -0) / 180}' 1.5708
対数、指数
AWKには対数も用意されていますが、用意されている関数log()はネイピア数eを底とする自然対数を返します。よく使う対数の表現として、10を底とした常用対数がありますが、こちらに変換するには学校で学んだように、次のようにして計算します。この場合、100は10の何乗であるかを求めていることになります。
$ awk 'BEGIN {print log(100) / log(10)}' 2
また、指数関数として関数exp()が用意されています。
$ awk 'BEGIN {print exp(1)}' 2.71828
以上のように、AWKに用意されているのはミニマルな関数のみです。数学的に変換するだけでまかなうことができる関数は用意されていない点に注意してください。
乱数
乱数を扱うために関数rand()が用意されていますが、このrand()関数は0から1までの乱数を発生されるものです。乱数なので、関数の出力結果はみなさんの環境と異なるかもしれません。
$ awk 'BEGIN {print rand()}' 0.237788
したがって、例えば1から10までの乱数が欲しい場合には、次のようにする必要があります。
$ awk 'BEGIN {print int(rand() * 10) + 1}' 3
ところが、このrand()関数は何度実行しても同じ値を返し、返す値はAWKをビルドした環境に依存します。
$ awk 'BEGIN {print int(rand() * 10) + 1}' 3 $ awk 'BEGIN {print int(rand() * 10) + 1}' 3
これでは期待する乱数として扱えそうにありません。そこで、乱数の種を初期化する関数としてsrand()関数が準備されています。これを用いることで、毎回異なる乱数を発生させることができます。
$ awk 'BEGIN {srand(); print int(rand() * 10) + 1}' 1 $ awk 'BEGIN {srand(); print int(rand() * 10) + 1}' 3
さて、このsrand()関数はビルドされた環境にも依存するのですが、本来の目的ではない使い方もできます。
LinuxのGCC等でビルドされた場合には、srand()関数は1970年1月1日からの経過秒数を種にするため、Unix時間を取得することができるのです。
$ awk 'BEGIN {print srand() + srand()}' 1399856514
変な記述方法ですよね。複雑なので本稿では説明を割愛しますが、これを用いることで、AWKの実行にかかった時間を取得したり、Unix時間から日時や時間を取得したりできます。
ただし、gawk(GNU AWK)ではUnix時間を直接取得する関数systime()や、時間計算を行う関数がありますので、こちらを使うのが便利でしょう。