正規表現・テキスト処理 ・ 中級〜上級awkで集計レポート
awkはパターン{アクション}という構造で行を処理する道具でしたが、ここに連想配列とENDブロックを組み合わせると、集計レポートを作る道具に変わります。count[$1]++でキーごとに数え、ENDで合計や平均を出し、printfで桁を揃えて見せる。アクセスログのIP別件数やステータス別集計といった実務の定番作業を、短い1行のコマンドでこなす感覚を身につけましょう。
awkの基本はパターン{アクション}という組み立てだった。条件に合う行を見つけたら、その行に対して何かをする。これまではフィールドを取り出して表示するくらいの使い方が中心だったはずだ。
だがawkの本当の力は、行を処理しながら「数を覚えておく」ことができる点にある。変数はコマンドを実行している間ずっと生き続けるので、1行目で覚えた値を100行目でも使える。
💡ポイントawkの変数は行をまたいで値を保持し続ける。この性質があるからこそ、全行を読み終えたときに「合計は何件でした」というレポートが作れる。
🔗たとえawkの集計は、駅の改札で乗客を1人ずつ数える駅員に似ている。1人通るごとに指を折って数え、最後にまとめて「今日は何人でした」と報告する。sedが1人ずつすれ違うだけの案内係だとすれば、awkは数え続ける駅員だ。
この「数え続ける」を実務で活かすカギが、連想配列という仕組みになる。
🗂️ 連想配列でキーごとに数える
ふつうの配列は0番目、1番目、と数字で場所を指定する。awkの連想配列は数字の代わりに文字列をキーとして使え、count["192.168.0.1"]のように書ける。
実務でよく使う型が count[$1]++ だ。$1は1番目のフィールド(多くの場合IPアドレスなど)を指し、そのフィールドの値をキーにして、対応する値を1増やす。同じ値が出てくるたびにカウントが積み上がっていく。
▶例awk '{count[$1]++} END{for (ip in count) print ip, count[ip]}' access.log を実行すると、IPアドレスごとのアクセス件数が一覧できる。
✓コツ連想配列のキーは$1だけに限らない。count[$1" "$9]のようにフィールドを連結すれば「IPアドレスとステータスコードの組み合わせごと」に数えることもできる。
🏁 ENDブロックで締めの発表をする
count[$1]++は1行読むたびに実行されるが、それだけでは配列が更新されるだけで、まだ何も画面に出ていない。全部の行を読み終えたあとに、初めて集計結果をまとめて出したい。
そこで使うのがENDブロックだ。END{ ... }と書くと、そのアクションは入力の最後まで読み終えたあと、1回だけ実行される。
ENDブロックの中ではfor (キー in 配列) という書き方で、連想配列に溜まった全キーを順番に取り出せる。この構文があるから、集計結果を一覧として出力できる。
▶例awk '{sum += $NF} END{print "合計:", sum, "平均:", sum/NR}' sales.txt は、各行の最終フィールドをsumに足し込み続け、読み終えたら合計と平均(NRは処理した行数)を表示する。
💡ポイントパターン{アクション}は行ごとに毎回実行され、END{アクション}は全行を読み終えた後に一度だけ実行される。この「毎回」と「最後に一度」の違いが、集計の骨組みを作る。
⚠つまずきENDのforでキーを取り出す順序は、配列に追加した順とは限らない。表示順序をそろえたいときは、いったんsortコマンドに渡すなど別の工夫が必要になる。
🖨️ printfで桁を揃えて読みやすくする
awkのprintは手軽だが、値の長さがバラバラだと列が揃わず、表としては見づらくなる。ここで役立つのがprintfだ。
printfは"%-15s %5d\n"のような書式を指定できる。%-15sは「左寄せで15文字幅の文字列」、%5dは「右寄せで5文字幅の整数」という意味になる。マイナスの有無で左寄せと右寄せが切り替わる。
🔗たとえprintは自由席の乗客が思い思いの位置に座るようなもの。printfは座席番号を指定して整列させるイメージだ。整列すれば見た瞬間に数字の大小が比較しやすくなる。
▶例awk '{count[$1]++} END{for (ip in count) printf "%-15s %5d\n", ip, count[ip]}' access.log なら、IPアドレスを15文字幅の左寄せ、件数を5文字幅の右寄せにして、桁の揃った表として出力できる。
✓コツ桁が思ったように揃わないときは、まず%sや%dの前後にある数字(幅指定)を見直すとよい。幅が短すぎると値がはみ出し、長すぎると余白が間延びして見える。
📊 実務のレポート例
こうした集計がよく使われる典型例が、Webサーバーのアクセスログだ。IPアドレス別のアクセス数、HTTPステータスコード別の件数など、ログの決まった位置にあるフィールドを数えるだけでレポートになる。
▶例awk '{count[$9]++} END{for (s in count) printf "%-5s %5d\n", s, count[s]}' access.log は、9番目のフィールドがステータスコードだと仮定して、200・404・500などのコードごとの件数を表にする。
⚠つまずきどのフィールドが何を表しているか($9が本当にステータスコードかどうか)はログの形式次第で変わる。実際に使うときは、まず1行を目で確認し、何番目のフィールドが何を意味するかを見極めてから集計コマンドを組み立てる。
🎉 短い1行が仕事をする楽しさ
こうしたawkの1行コマンドは、英語圏でone-liner、日本語で「1行野郎」などとも呼ばれ、遊び心を持って語られることがある。パイプでつないだたった1行が、専用の集計スクリプトを書くのと同じ結果を出す。
最初はcount[$1]++やEND{for(...)}の意味がすぐには飲み込めないかもしれない。だが何度か手を動かすうちに、「集計したいものをキーにする」「読み終えたらENDでまとめる」という発想が自然に浮かぶようになる。
💡ポイントcount[キー]++、END{for(...)}、printfでの整形。この3点セットを覚えておけば、多くの集計レポートはawkの1行で済ませられる。
次のトピックでは、こうした集計や抽出の材料になる「よく出てくるパターンそのもの」、IPアドレスや日付や時刻の形を読み解く実務パターン集に進んでいく。
この項目に出てくる用語
連想配列れんそうはいれつ数字ではなく文字列などをキーにして値を格納できる配列。awkのcount[$1]++などで使う。
ENDブロックえんどぶろっくawkで全ての行を読み終えた後に一度だけ実行されるアクション。END{...}と書く。
1行野郎いちぎょうやろうパイプやコマンド1行だけで、まとまった処理を済ませてしまう短いコマンドの俗称。
関連コマンド
▶ 学習アプリでこの続きを学ぶ・演習する