awk歴一日の初心者が語るawk理解のポイント

今までテキスト処理などでワンライナーを使うときはperlを使っていた.Perlを先に覚えた身としてはawksedは過去の産物だと思い込んでいた.awkの方が簡潔に書けるケースに出くわしたことをきっかけにawkを勉強してみたら単な食わず嫌いだったことに気が付いたのでポイントをまとめてみる.

なおプログラミング言語としてはAWKという表記が正しく (Aho, Weinberger, Kernighanの頭文字! ということだけは以前から知っていた),オリジナル処理系のことをawkと呼ぶのだろうけれど,今回は処理系の違いは気にしないのでawk, nawk, gawkの違いは気にしないことにした.ていうか詳しく知らない.

awkってなんぞ?」という方が対象読者.スクリプト言語正規表現の基礎知識が少しでもあれば,たぶんすぐに使えるようになると思う.awkを一度でも使ったことがある人には新しい情報はほとんどないと思う.


帰宅してからずっと本棚の肥やしになっていたsed & awkプログラミング 改訂版 (A nutshell handbook)を読んでいて

入力データの「こんな行」に対して,「こんな処理」をしなさいという手続きを書く行指向のプログラミング言語

というイメージが沸いた瞬間,awkを好きになれる気がした.


更に付け加えると入力データは区切り文字 (デフォルトは空白.変更可能) で区切られていることを想定しているので

各行は区切り文字で分割して変数($1, $2, ...) に格納しておいてくれる

という点がとても便利.Perlだとイチイチsplitしなければならないので.(実はそんなことないののだけど後述)


awkのコードは以下の3部分に分かれる.

BEGIN { 前処理 }
条件部 { 処理 }
END { 後処理 }


メインブロックは複数あってもよい

BEGIN { 前処理 }
条件部 { 処理 }
条件部 { 処理 }
条件部 { 処理 }
...
END { 後処理 }


日本語で読みかえると,こんなイメージ

BEGIN { 最初にこんなことする }
こんなレコードに { こんなことする }
あんなレコードに { あんなことする }
そんなレコードに { そんなことする }
END { 最後にそんなことする }


ワンライナーで書くときはこんな感じになる.

% awk 'BEGIN{ 前処理 } 条件部 { 処理 } END{ 後処理 }'

入力データはファイル名を指定してもよいし,標準入力から与えてもよい.

見てのとおり,BEGINブロックはプログラム開始時に一度だけ実行され,ENDブロックはプログラム終了時に一度だけ実行される.それぞれ変数の初期化と結果の計算や出力などに利用したりする.

各レコードの調理方法は真ん中の条件部とその処理.入力されたテキストの各レコード (デフォルトではレコード区切りは改行なので,各行) について条件部{ 処理 } を逐次適用するイメージ.

なお,条件部と処理は複数記述してもよい.複数条件部が記述された場合は,1レコードについて全ての条件部のチェックをするので,Perlでいうところの if {} elsif {} elsif ... ではなく,if {} if {} if {}.


いま言葉で説明したことを図にするとこんな感じ.入力データの各行に対してメインブロックが逐次適用されているイメージがつくかと思う.

名前,身長,体重が記述されているデータに対して,身長が170より大きい人の体重の平均,160より大きい人の体重の平均を計算するawkワンライナーの例.


以降,ワンライナーの例をいくつか紹介する.この条件部の記法がかなり便利.よく使う条件部の指定方法として正規表現がある.

# 先頭が1の行を表示
% awk '/^1/ {print}' hoge.txt

なお,条件部と処理は複数記述してもよい.

% awk '/^1/ {a++} /^2/ {b++} END{print a; print b;}' hoge.txt

この場合,先頭が1で始まる行と2で始まる行の数をそれぞれa, bという変数に格納し,最後に出力している.Perl使いがawkを使い始めると変数のはじめに$記号をつけてしまいがちだけれど,$で始まるのはフィールド変数だけ,他は$が付かないので注意.


条件部の指定は,正規表現以外も利用することができる.たとえばカンマ区切りのCSVデータを入力として受け取り,2番目のカラムが100以上の行数を数えたいとする.こんな感じで書ける.

% awk -F, '$2>100 {count++} END{print count}' hoge.txt

ここでは-Fオプションで区切り文字を指定している.count変数はBEGINブロックで初期化してもよいけれど,デフォルト初期値は0のようなので省略している.


その他には,例えば必ず3行ずつ出力するプログラムが標準出力にひたすら結果を出力するような場合,かつ,そのうち1行目のみが必要な場合,入力データを3行おきに処理する必要がある

その場合は,

% awk 'NR % 3 == 0 { print }' hoge.txt

というように条件部を記述することができる.ここでNRは現在処理している行番号のこと.


よく使う組み込み変数

  • NR: 処理対象の行番号
  • NF: 処理対象のフィールド数
  • FS: 区切り文字 (-Fオプションでも指定可能)

などがある.特にデータによってカンマ区切りだったり,タブ区切りだったり異なるので,区切り文字の指定は覚えておくと便利かもしれない.

本記事はawk解説が目的ではないので,詳しいawkの使い方については,最後のリンク集を参考のこと

ポイントのまとめ

初心者は以下の3点を理解すると,awkを使えるようになる (気がする)

  • awkは行指向のプログラミング言語
  • 処理対象の行を条件部で指定して,その行に対する処理を記述する
  • 前処理や後処理がしたい場合は適宜BEGINブロック, ENDブロックを利用する

え? それPerlでできるよ?

ここまで書いておいてアレな感じだけれど同じような書き方はPerlでもできたりする.ワンライナーの-eオプションに加えて,各行を処理する-nオプション,awkの$1, $2相当のことを行う-aオプションを使えばほぼawkの感覚で書ける

おすすめリンク集

書籍であれば,これがおすすめ (というかこれしか知らない)

sed & awkプログラミング 改訂版 (A nutshell handbook)

sed & awkプログラミング 改訂版 (A nutshell handbook)

次はsedを使えるようになりたいなぁ.