久しぶりのAWKシリーズ第5回目、今回はAWKで数値演算を試してみましょう。
四則演算
AWKでは一般的な四則演算の数式をそのまま記述することで、計算結果を出力することが可能です。
例えば、((198 * 256) + (214 * 319)) * 18 - 931
を計算してみると2140241
となりますが、実際にこの数式をawkコマンドで計算してみると
1 2 |
# awk 'BEGIN {print ((198 * 256) + (214 * 319)) * 18 - 931}' 2140241 |
結果が正しく出力されました。
数式はコマンドの引数として計算することも可能です。
こちらをcalc.sh
として保存して
1 2 3 |
#!/bin/bash awk "BEGIN {print $*}" |
このスクリプトに数式を引数として実行すると
1 2 |
# bash ./calc.sh '((198 * 256) + (214 * 319)) * 18 - 931' 2140241 |
ご覧のように計算結果が出力されます。
数値演算関数
AWKでは三角関数、指数や対数の数値演算系の関数が用意されています。
これらを用いればもっと複雑な計算もできそうです。
三角関数
正弦を表すsin()
関数と余弦を表すcos()
関数があります。ただし、正接を表す(tan)関数は存在しません。正接を導き出す際にはsin を cos で割ります
。
1 2 |
# awk 'BEGIN {print sin(1) / cos(1)}' 1.55741 |
また、sin()
とcos()
に渡す引数は角度(deg)ではなくラジアン(rad)です。
角度からラジアンへの変換式はrad = deg * π / 180
です。
このπ
は属に3.14
という決め打ちの値になりがちですが、atan2()
関数を用いて計算することが可能です。
atan2
の細かい部分は割愛しますが、ここではatan2(0, -0) = π
になることがポイントです。
1 2 |
awk 'BEGIN {print atan2(0, -0)}' 3.14159 |
これを使えば任意の角度のラジアンを求めることができます。
例えば、45度では
1 2 |
# awk 'BEGIN {print 45 * atan2(0, -0) / 180}' 0.785398 |
という風にrad = deg * π / 180
の式に当てはめるだけです。
指数、対数
ネイピア数e
を底とする対数をlog()
関数で表現でき、e
の指数関数はexp()
として表現できます。
複雑は数式に当てはめるところがあれば使えるかもです。(多分使わないでしょう・・
1 2 |
# awk 'BEGIN {print exp(1)}' 2.71828 |
1 2 |
# awk 'BEGIN {print log(256)/log(2)}' 8 |
乱数
rand()
関数を使って0 ~ 1 (0 <= rand < 1)までの乱数を生成できます。・・・が、何度実行しても同じ値になるため、事前にsrand()
関数で乱数の種となるものを初期化する必要があります。
1から100までの乱数を出力したい場合はこのように表現します。
1 2 |
# awk 'BEGIN {srand(); print int(100 * rand()) + 1}' 77 |
int()
関数は少数のうち、整数部分を抜き出すのに使えます。
練習問題
そんなこんなで、数値計算を使った問題を解いてみましょう。
問題1
以下の教育テストのCSVデータ(test.csv)を用います。
1 2 3 4 5 |
name,kokugo,sugaku,eigo,rika,shakai tarou,89,70,80,85,75 hanako,77,90,83,70,85 takashi,85,80,73,80,81 shingo,63,75,98,68,89 |
- 全員の合計点を"名前 合計点"のフォーマット(スペース区切りで良い)で出力してください。
sort
コマンドを利用して合計点が降順にして1の結果を出力してください。- 平均点(整数)を"名前 平均点"のフォーマットにし、
sort
コマンドを利用して平均点の降順で出力してください。
問題2
以下の辺a, 辺bおよびaとbの角度を表す三角形のCSVデータ(sankaku.csv)を用います。
1 2 3 4 |
edge_a,edge_b,angle 14,18,45 9,21,28 30,30,79 |
atan2()
関数を用いて角度(deg)をラジアン(rad)に変換して出力してください。* 角度とラジアンの変換関数はrad = deg * π / 180
である。- それぞれの三角形の面積を出力してください。* 三角形の面積(S)は
S = (a* b * sin(θ)) /2
で求められる。
問題3 (難)
次の大アルカナの表(arcana.txt)を用いて以下の仕様をみたすタロット占いを行うスクリプト(arcana.sh)を作成してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
0 THE_FOOL 1 THE_MAGICIAN 2 THE_PRIESTESS 3 THE_EMPRESS 4 THE_EMPEROR 5 THE_HIEROPHANT 6 THE_LOVERS 7 THE_CHARIOT 8 STRENGTH 9 THE_HERMIT 10 WHEEL_OF_FORTUNE 11 JUSTICE 12 THE_HANGDE_MAN 13 DEATH 14 TEMPERANCE 15 THE_DEVIL 16 THE_TOWER 17 THE_STAR 18 THE_MOON 19 THE_SUN 20 THE_JUDGEMENT 21 THE_WORLD |
- 実行に用いる引数は無し(ファイル名のみで実行)
- カード番号(0から21)を表す乱数r1と正位置(1~5)、逆位置(6~10)を表す乱数r2を用いる
- 出力形式は
アルカナ名 位置
で表す。例:JUSTICE POSITIVE
できましたかね・・それでは解答例はこちら!
問題1
1 全員の合計点を"名前 合計点"のフォーマット(スペース区切りで良い)で出力してください。
1 2 3 4 5 |
# cat test.csv | awk -F ',' 'NR>=2{print $1, $2+$3+$4+$5+$6}' tarou 399 hanako 405 takashi 399 shingo 393 |
CSVを読み取っているのでフィールドパターンはカンマ(,)で2行目から出力するための条件としてNR>=2
をつけましょう。
2 sortコマンドを利用して合計点が降順にして1の結果を出力してください。
1 2 3 4 5 |
# cat test.csv | awk -F ',' 'NR>=2{print $1, $2+$3+$4+$5+$6}' | sort -k 2 -r hanako 405 tarou 399 takashi 399 shingo 393 |
1で出力した結果をパイプで繋いでsort
コマンドに渡してやればOKです。
-k 列番号
でソートしたい列を指定、-r
で昇順の反対である降順を指定できます。
3 平均点(整数)を"名前 平均点"のフォーマットにし、sortコマンドを利用して平均点の降順で出力してください。
1 2 3 4 5 |
# cat test.csv | awk -F ',' 'NR>=2{print $1, ($2+$3+$4+$5+$6) / 5}' | sort -k 2 -r hanako 81 tarou 79.8 takashi 79.8 shingo 78.6 |
2の計算式の部分を5で割ってやればOKです。
問題2
1 atan2()関数を用いて角度(deg)をラジアン(rad)に変換して出力してください。
1 2 3 4 |
# cat sankaku.csv | awk -F ',' 'NR>=2{print $3 * atan2(0, -0) / 180}' 0.785398 0.488692 1.37881 |
計算式に3列目を割り当てるだけですね。
それぞれの三角形の面積を出力してください。
1 2 3 4 |
# cat sankaku.csv | awk -F ',' 'NR>=2{rad=($3 * atan2(0, -0) / 180);print $1 * $2 * sin(rad) / 2}' 89.0955 44.3651 441.732 |
1の計算結果をrad
という変数に格納して、計算式に当てはめれば求められます。
問題3
シェルスクリプトの知識と、AWKでの条件分岐の知識など必要なのでググりながらやりましょう。
1 2 3 4 5 6 7 8 |
#!/bin/bash r1=$(awk 'BEGIN {srand(); print int(rand() * 22) + 1}') # arcana.txtは1~22行なので r2=$(awk 'BEGIN {srand(); print int(rand() * 10) + 1}') arcana=$(cat arcana.txt | awk 'NR=='"$r1"' {print $2}') position=$(awk 'BEGIN {if(1 <= '"$r2"' && '"$r2"' <= 5){result="POSITIVE"}else{result="NEGATIVE"};print result}') echo "$arcana $position" |
r1
に1~22までの乱数(アルカナの番号は0 ~ 21ですが、arcana.txtの該当する行を抽出するため)、r2
に1~10までの乱数を入れます。
変数arcana
にaracana.txt
を読み込んでr1
の行番号になったものを出力して格納し、
変数position
にr2
が1 ~ 5なら"POSITIVE"、6 ~ 10なら"NEGATIVE"を格納し、
最後にそれらを"アルカナ名 位置"で出力すれば完成です。
コマンドであり軽量言語(LL)の元祖でもあって、
シェルでのテキストデータ処理には便利で手放せない
「AWK」の魅力と書き方、シェルコマンドと組み合わせた
テクニック(シェル芸)を解説!