awk歴一日の初心者が語るawk理解のポイント
今までテキスト処理などでワンライナーを使うときはperlを使っていた.Perlを先に覚えた身としてはawkやsedは過去の産物だと思い込んでいた.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を使えるようになる (気がする)
え? それPerlでできるよ?
ここまで書いておいてアレな感じだけれど同じような書き方はPerlでもできたりする.ワンライナーの-eオプションに加えて,各行を処理する-nオプション,awkの$1, $2相当のことを行う-aオプションを使えばほぼawkの感覚で書ける
おすすめリンク集
- awk基礎文法最速マスター
- さらっと基本をおさえる
- AWKの第一歩 (PDF)
- これの2章,5章をさらっと読めばたいていのことはできるようになるはず
- awk sed 入門
- 組み込み変数,関数一覧
- プログラミング言語awk
- こちらも1ページで簡潔にまとめっている
- AWKならどう書く?
- awkのクックブック相当?
- 文系のためのawkプログラミング入門
- 読み物的に
- AWKのススメ - クックパッド開発者ブログ
- 実例を紹介
- 実例でわかるAWK 第1回
書籍であれば,これがおすすめ (というかこれしか知らない)
sed & awkプログラミング 改訂版 (A nutshell handbook)
- 作者: Dale Dougherty,Arnold Robbins,福崎俊博
- 出版社/メーカー: オライリー・ジャパン
- 発売日: 1997/10/01
- メディア: 大型本
- 購入: 2人 クリック: 48回
- この商品を含むブログ (34件) を見る
次はsedを使えるようになりたいなぁ.