awkの理解を深める7つのコマンド


はじめに

今やっているプロダクトは、いろんな外部システムとtsvやcsvなどをIFファイルとしてデータ連携しているので、テストの際に「awk」をよく使っています。

それをプロダクト内で実施している勉強会で話したら、結構反響があったのでまとめておきます。

個人的な偏見ですが、このあたりを知っていれば中級レベルだと言えると思います。


初級編

実際にコマンドを見て理解していくのがいいと思います。

最後に構文について触れます。

$ cat file.tsv 

hoge 1 2 3
fuga 4 5
moge 2 3 4
gaga 5 4 2

仮にこんなtsvファイルに対し、awkでできることを列挙していきます。


① n列目のデータを表示する

$ awk -F"¥t"'{print $2}' file.tsv 

1
4
2
5

今回は2列だけを出力しています。

これを3列目や4列目にしたいなら'{print $3}''{print $4}'とすればOKです。


② 2つ以上の項目を区切り文字を指定して出力する

$ awk -F"\t" -v "OFS=," '{print $1,$2}' file.tsv 

hoge,1
fuga,4
moge,2
gaga,5

1列目と2列目を「,」区切りで出力しています。

複数出力は{print $1, $2, …}と記載し、出力時の区切り文字は-v "OFS=XXX"でしてしています。

今回は割愛しますが、-vで指定できる設定値は他にもあります。


③ 出力フォーマットを指定して出力する

$ awk -F"\t" '{printf "no2: '%s', no3: '%s' \n", $2, $3}' file.tsv 

no2: 1, no3: 2
no2: 4, no3: 5
no2: 2, no3: 3
no2: 5, no3: 4

他の言語でもありますが、printfでフォーマット指定です。

Javaとか触ったことあればイメージできると思います。


④ ある条件の場合のみ出力する

$ awk -F"\t" '($1=="fuga"){print}' file.tsv 

fuga 4 5

{}の前に条件文を書くことが可能です。

今回であれば、1列目の値が"fuga"だったら{print}を実行となっています。

結果、2行目だけ出力されています。(printはまま1行出力する)

'{if($1=="fuga") print}'と書いても大丈夫です。


中級編

ここから中級編です。

一旦ここでファイルの中身を確認しておきます

$ cat file.tsv 

hoge 1 2 3
fuga 4 5
moge 2 3 4
gaga 5 4 2


⑤ 2列目の最大値を出力する

$ awk -F"\t" '(m<$2){m=$2} END{print m}' file.tsv 

5

新たにmENDが登場してきました。

mはただの変数です。デフォルトでは空文字が入っています。

ENDはすべての行の処理が終わった後に実行する文です。

なので、mより$2が大きい場合m$2を代入し、すべての行の処理が終わった後にmを出力しています。


⑥ 2列目の最小値を出力する

$ awk -F"\t" 'BEGIN{m=100} {if(m>$2) m=$2} END{print m}' file.tsv 

1

⑤とほとんど同じです。

BEGINが必要な理由は、空文字<数値となるためです。

※もし空文字を含む4列目の最小値の場合$ awk -F"\t" 'BEGIN{m=100} {if($4!="" && m>$4) m=$4} END{print m}' file.tsvとすればOKです


⑦ 最小値、最大値を出力する

$ awk -F"\t" 'BEGIN{m=100} {if(m>$2) m=$2} {if(n<$2) n=$2} END{print m, n}' file.tsv 

1 5

{if()…}などは複数書くことができます。


まとめ

awkの構文は、awk -F"区切り文字" -v "設定名=XX" 'パターン {実行文}' fileとなっています。

awkはファイルを1行ずつ処理していき、それをパターン {実行文}に沿って処理していきます。

なので⑦の例であれば、以下ソースを実行していると同義になります。

// file.tsv のデータ

var rows = {
["hoge", 1, 2, 3],
["fuga", 4, 5],
["moge", 2, 3, 4],
["gaga", 5, 4, 2]
};

// BEGIN処理
var m = 100;

// main処理
for (rows as row) {
// {if(m>$2) m=$2} の処理
if(m>row[2]) {
m=row[2]
}
// {if(n<$2) n=$2} の処理
if(n<row[2]) {
n=row[2]
}
}

// END処理
print(m, n);

こうするとイメージしやすいと思います。

このあたりを理解しておけば、awkで困ることはないです。(少なくても困ったことはない。)