SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

USP MAGAZINEコラボレーション連載/「シェル芸」に効く AWK処方箋

シェルで表計算ができるかも!? AWKの四則演算と数値演算関数

► 「シェル芸」に効く AWK処方箋 第5回 (月刊『USP MAGAZINE 2014 August (Vol.16)』より転載)


  • このエントリーをはてなブックマークに追加

 今回は、AWKで行う「数値演算」を解説します。AWKはテキスト処理に特化しているため、数値演算が苦手というイメージをお持ちの方が多いかもしれません。しかし、実は一般的な四則演算に加えて、多くの数値演算関数が実装されています。加えて本稿では、AWKをはじめとする多くの言語が2進数で計算を行っていることにより直面する問題に言及し、AWKがどこまでの数を計算できるのか検証します。

  • このエントリーをはてなブックマークに追加

四則演算

 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()や、時間計算を行う関数がありますので、こちらを使うのが便利でしょう。

本連載が単行本になりました!

「シェル芸」に効く!AWK処方箋

Amazon  その他

「シェル芸」に効く!AWK処方箋

著者:斉藤博文
発売日:2017年1月31日(火)
価格(POD):2,160円(税込)
価格(電子書籍):1,728円(税込)

本書について

 コマンドであり軽量言語(LL)の元祖でもあって、シェルでのテキストデータ処理には便利で手放せない「AWK」の魅力と書き方、シェルコマンドと組み合わせたテクニック(シェル芸)を解説。
 プリントオンデマンド(POD)と電子書籍にて、絶賛発売中です!

会員登録無料すると、続きをお読みいただけます

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

次のページ
整数化

この記事は参考になりましたか?

  • このエントリーをはてなブックマークに追加
USP MAGAZINEコラボレーション連載/「シェル芸」に効く AWK処方箋連載記事一覧

もっと読む

この記事の著者

斉藤 博文(サイトウヒロフミ)

最初にAWKと出会ってから○十年、AWKの魅力に取りつかれ、勢い余って「日本 GNU AWKユーザー会」を立ち上げています。会としてOSCなどのイベントにも出展しつつ、GNU AWKの開発も手伝っています。「USP友の会」では幹事役ですが、「シェル芸勉強会」にはほぼ毎回参加して一緒に勉強しています。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/8025 2017/01/31 20:03

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング