こんにちは。
Zucks Affiliateというサービスの開発・運用を行っている宗岡です。

皆様、昨年末のTHE MANZAIはご覧になられましたでしょうか。
優勝した博多華丸・大吉は本当に面白かったですね。

そこで、今回は「M1・THE MANZAIの結果で学ぶawk入門」というテーマで書いてみたいと思います。

今回は、サンプルのテキストファイルを元にawkでどんな事が出来るのか、何が便利になるのかを改めて語ってみたいと思います。

題材としては、過去のM1と、THE MANZAIで優勝した芸人達の、所属事務所や、結成年度を使います。
これらを元に、awkを使うと、いとも簡単に欲しいデータが手に出来てしまうということを実践していきたいと思います。

サンプルとして使うファイル (m1_theman.txt)

開催年度 優勝者 所属事務所 結成年度
2001 中川家 よしもと 1992
2002 ますだおかだ 松竹芸能 1993
2003 フットボールアワー よしもと 1999
2004 アンタッチャブル 人力舎 1994
2005 ブラックマヨネーズ よしもと 1998
2006 チュートリアル よしもと 1998
2007 サンドウィッチマン グレープカンパニー 1998
2008 NONSTYLE よしもと 2000
2009 パンクブーブー よしもと 2001
2010 笑い飯 よしもと 2000
2011 パンクブーブー よしもと 2001
2012 ハマカーン ケイダッシュステージ 2000
2013 ウーマンラッシュアワー よしもと 2008
2014 博多華丸・大吉 よしもと 1990

では、このファイルを使ってawkの便利さを実感していきましょう。

の前に、今回はawk入門なので、基礎知識についても一応書いておこうと思います。

0. awkの基礎

awkは、下記の3つをテキストの最後の行まで繰り返し行うもので、3の「処理を行う」部分を自分で書いていきます。
  1. テキストから1行を読み込み
  2. 読み込んだ行を空白区切りのデータと解釈し
  3. 処理を行う

この読み込んだ1行の事をレコードと言い、レコードの中に空白で区切られた1つ1つの項目をフィールドといいます。


各フィールドは、$1, $2のように”$”の後にフィールドの番号を付ける事で、各フィールドを表すことが出来ます。


awkには特殊な変数もあり、$0はレコード全体を表し、NRは行数、NFはフィールド数を表します。

これでawkの基礎はバッチリです。


1. 特定のフィールドだけ表示する

では、早速やっていきましょう。
試しに開催年度と、優勝者だけを表示してみましょう。

開催年度と優勝者はそれぞれ、1列目と2列目のフィールドに記載されているので、$1と$2を表示あげれば行けそうです。

$awk '{print $1":"$2}' m1_theman.txt
開催年度:優勝者
2001:中川家
2002:ますだおかだ
2003:フットボールアワー
2004:アンタッチャブル
2005:ブラックマヨネーズ
2006:チュートリアル
2007:サンドウィッチマン
2008:NONSTYLE
2009:パンクブーブー
2010:笑い飯
2011:パンクブーブー
2012:ハマカーン
2013:ウーマンラッシュアワー
2014:博多華丸・大吉

簡単ですね。

2. 優勝した時の結成年数を計算して表示してみる

awkはプログラムとしても書けるので、処理を書いたものを用意してみます。
また、awkは普通に四則演算も出来るので、今年のコンテストの開催年度から結成年度を引いて、優勝した時点でのコンビの結成年数を出してみます。
$cat test.awk
{
    printf("%s年: %s(結成%s年)\n", $1, $2, $1-$4)
}

拡張子は何でもいいですが、awkプログラムだとわかりやすいようにawkとしてます。
では早速これを使って、開催年度、優勝者、結成年数を表示してみます。

$awk -f test.awk m1_theman.txt
開催年度年: 優勝者(結成0年)
2001年: 中川家(結成9年)
2002年: ますだおかだ(結成9年)
2003年: フットボールアワー(結成4年)
2004年: アンタッチャブル(結成10年)
2005年: ブラックマヨネーズ(結成7年)
2006年: チュートリアル(結成8年)
2007年: サンドウィッチマン(結成9年)
2008年: NONSTYLE(結成8年)
2009年: パンクブーブー(結成8年)
2010年: 笑い飯(結成10年)
2011年: パンクブーブー(結成10年)
2012年: ハマカーン(結成12年)
2013年: ウーマンラッシュアワー(結成5年)
2014年: 博多華丸・大吉(結成24年)
1レコード目の「開始年度」「優勝者」などが邪魔ですね。
今回の結果に合わせたフィールド名を表示してみましょう。

awkでは処理の初めに1回だけ実行したいものは、BEGINという部分に書けばOKです。
(処理の最後に1回だけ実行したいものはENDに書きます)

また、中括弧”{}”の前に条件を書けば、条件に一致したレコードにだけ処理をさせることも可能ですので、この2つを使っていい感じに表示してみます。

$cat test.awk
BEGIN {
    print "開催年度:" "優勝者(結成年数)"
}
# 2行目以降だけ処理する
NR > 1 {
    printf("%s年: %s(結成%s年)\n", $1, $2, $1-$4)
}
1行目に出すフィールド名に関しては、BEGINに書き、2行目以降にだけ処理を適用するのに"NR > 1"と書いています。
(NRは一番初めに出てきた行数を表す特殊変数です)
$awk -f test.awk m1_theman.txt
開催年度:優勝者(結成年数)
2001年: 中川家(結成9年)
2002年: ますだおかだ(結成9年)
2003年: フットボールアワー(結成4年)
2004年: アンタッチャブル(結成10年)
2005年: ブラックマヨネーズ(結成7年)
2006年: チュートリアル(結成8年)
2007年: サンドウィッチマン(結成9年)
2008年: NONSTYLE(結成8年)
2009年: パンクブーブー(結成8年)
2010年: 笑い飯(結成10年)
2011年: パンクブーブー(結成10年)
2012年: ハマカーン(結成12年)
2013年: ウーマンラッシュアワー(結成5年)
2014年: 博多華丸・大吉(結成24年)
いい感じになりました。

3. 優勝コンビの平均結成年数を出してみる

ここ14年間色んなコンビが優勝してきた訳ですが、
ここまで来ると、一体結成何年くらいのコンビが優勝してるのか、
つまり優勝コンビの平均結成年数が何年かが気になってきます。
という事でこれも計算して出してみます。

実はawkでは変数も扱う事が出来るので、その練習として、優勝コンビの平均結成年数を出して見たいと思います。

$cat test.awk
BEGIN {
    sum = 0
}
NR > 1 {
    # 結成年数をsumにインクリメントしてく
    sum += $1 - $4
}
END {
    printf("平均結成年数: %d年\n", sum/(NR-1))
}

BEGINで処理の最初にsumという変数を用意して、ENDで処理の最後に平均を計算しています。

$awk -f test.awk m1_theman.txt
平均結成年数: 9年

意外と若手。

と、このような事実もawkを使えばあっという間に分かってしまいます。

4. 所属事務所毎の優勝回数を出してみる

awkの便利なところはこれだけでは有りません。
普通に、配列も使えて、forやwhileなどの制御構文も使えるのです。
では、この配列と制御構文を使って優勝コンビの所属事務所毎の優勝回数を出してみます。

$cat test.awk
BEGIN {
    # resultを初期化
    split("", result)
    print "所属事務所:" "優勝回数"
}
NR > 1 {
    # 所属事務所を連想配列のキーにしてインクリメント
    result[$3]++;
}
END {
    for (agency in result) {
        printf("%s: %d回\n", agency, result[agency])
    }
}

splitというのはawkの組み込み関数で、第一引数に受け取った値を指定された区切り文字で分割して配列にするものです。

$awk -f test.awk m1_theman.txt 所属事務所:優勝回数
よしもと: 10回
グレープカンパニー: 1回
松竹芸能: 1回
ケイダッシュステージ: 1回
人力舎: 1回

圧倒的よしもと。
こういった処理もawkなら簡単です。

5. 結成10年以上で優勝したコンビを出してみる

最後に、awkでは自分で関数を定義することも出来るので、先ほどの結成年数を計算する部分を関数にして、結成10年以上のコンビだけ出してみたいと思います。

$cat test.awk
# 結成年数を返す
function getYear(combinedYear) {
    return $1 - combinedYear
}
BEGIN {
    print "優勝年度:" "優勝者(結成年数)"
}
NR > 1 {
    year = getYear($4)
    # 結成年数が10年以上の場合だけ表示
    if (year >= 10) {
        printf("%s: %s(%d年)\n", $1, $2, year)
    }
}
$awk -f test.awk m1_theman.txt
優勝年度:優勝者(結成年数)
2004: アンタッチャブル(10年)
2010: 笑い飯(10年)
2011: パンクブーブー(10年)
2012: ハマカーン(12年)
2014: 博多華丸・大吉(24年)

博多華丸・大吉、大ベテランですね。

まとめ

今回は入門編という事で簡単な例でawkの便利さを伝えてみましたが、
これだけでも色々なテキストの処理が簡単に行える事が想像付くかと思います。

実務においても、ログファイルの解析などを行う際にawkを使ってみると色々捗るかと思います。
先月までの私のように、awkの便利さを知らなかった方がいらっしゃいましたらこの機会に使ってみてはいかがでしょうか。


[PR]
VOYAGE GROUPではアドテクエンジニアを絶賛募集中です。
ご興味ある方は下記サイトもしくは @tech_voyage にご連絡ください。

https://voyagegroup.com/adtechunit/