Claude Code awkでログ集計を書く — 区切りの確認とTop Nの検算
アクセスログのIP別Top Nをawkで集計させるとき、先頭数行での区切り文字の確認と、sort | uniq -cとの二通りでの検算までをClaude Codeに任せる手順です。
アクセスログのIPアドレス別Top Nは、awkなら1行で書けます。ただし、1行で書けるぶん、区切り文字を読み違えても気づかないまま「それらしい表」が出てきます。Claude Codeにawkを書かせるときは、コードの出来より先に、数字が正しいと言える根拠をどう取るかを決めておくのが肝です。
この記事では、サンプルのアクセスログでIP別のTop Nを出し、区切り文字の確認、sort | uniq -c との突き合わせ、行数との照合という3つの検算を、Claude Codeの作業手順に組み込みます。
先頭数行で区切り文字を決めてから書かせる
awkは既定で空白(連続する空白もひとまとめ)でフィールドを分けます。Apacheなどのcombined形式のログなら、先頭が $1 でクライアントIPになります。しかし、ログの形式が思っていたものと違えば、$1 は別の何かです。
最初のプロンプトでは、集計コードを求めず、まず観察を求めます。
access.log の先頭5行を head で見て、区切り文字とフィールド数を教えて。
集計のコードはまだ書かないで。head は、権限ドキュメントにBashの組み込み読み取り専用コマンドとして載っているものの1つです。プロンプトなしで動く例に ls、cat、grep、wc なども並びます。観察の段階で毎回許可を求められることは少なく、会話のテンポが落ちません。
今回のサンプルログは次の7行です(IPはドキュメント用の予約アドレスです)。
203.0.113.5 - - [28/Oct/2026:10:00:01 +0900] "GET /index.html HTTP/1.1" 200 512
203.0.113.5 - - [28/Oct/2026:10:00:02 +0900] "GET /a.css HTTP/1.1" 200 128
198.51.100.7 - - [28/Oct/2026:10:00:03 +0900] "GET /index.html HTTP/1.1" 200 512
203.0.113.5 - - [28/Oct/2026:10:00:04 +0900] "POST /login HTTP/1.1" 302 0
192.0.2.9 - - [28/Oct/2026:10:00:05 +0900] "GET /index.html HTTP/1.1" 404 0
198.51.100.7 - - [28/Oct/2026:10:00:06 +0900] "GET /a.css HTTP/1.1" 200 128
203.0.113.5 - - [28/Oct/2026:10:00:07 +0900] "GET /index.html HTTP/1.1" 200 512先頭3行だけ $1 を出してみると、期待どおりIPが取れます。
head -n 3 access.log | awk '{print $1}'203.0.113.5
203.0.113.5
198.51.100.7巨大なログでいきなり全件を流さず、head -n で切った入力でawkのプログラムを試すのが基本です。出力が数行で済むので、$1 がIPかどうかを目で確認でき、Claude Codeにもそのまま結果を読ませられます。
IP別のTop Nを書かせる
区切りが確定したら、集計を頼みます。連想配列に件数をためて、END節で出力し、降順に並べます。
awk '{c[$1]++} END{for(k in c) print c[k], k}' access.log \
| sort -rn | head -n 24 203.0.113.5
2 198.51.100.7awkの for (k in c) が配列を巡回する順番は決まっていないので、並べ替えは後段の sort -rn に任せます。Claude Codeが「awkの中で並べ替えようとして複雑なコードを書く」場合は、集計と並べ替えを分けるよう一言添えると、読みやすい1行に戻ります。
プロンプトに入れておくとよいのは、次の3点です。
- 入力ファイルのパスと、確認済みのフィールド位置(
$1がIP) - Top Nの件数と、同数のときの扱い(同数は任意の順でよい、など)
- 結果は標準出力に出し、ファイルは書き換えない
3点目は安全面の話でもあります。awkはファイルへの出力も書けるため、集計の目的ではリダイレクトを使わないと明示しておけば、入力ファイルを誤って上書きするのを防げます。
条件で絞り込むときも手順は変わりません。たとえばステータスコードが404の行だけを数えるなら、$9 を条件にします。サンプルでは 192.0.2.9 の1件だけが出ます。
awk '$9==404{c[$1]++} END{for(k in c) print c[k], k}' access.log絞り込みのたびに $9 がステータスコードかどうかを、先頭数行で確かめ直すよう頼んでおくと、位置の数え間違いを防げます。
検算その1: sort | uniq -cと突き合わせる
awkの結果が正しいかを、同じ答えを別の方法で出して確かめます。sort | uniq -c は、集計ロジックがawkに依存しないので、突き合わせの相手として適しています。
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -n 2 4 203.0.113.5
2 198.51.100.7件数も順位も一致しました。この確認で見つかるのは、たとえば $1 を取り違えた、ヘッダー行を巻き込んだ、といった食い違いです。2つの方法が同じ誤りをするとは考えにくく、一致すれば集計の骨格は信頼できます。
Claude Codeへの頼み方は、「awkの結果を、別の方法でも出して比較して。差があれば原因を調べて」です。比較そのものをコマンドにさせたいなら、diff <(...) <(...) の形でも書けますが、出力が短いうちは目視でも足ります。
検算その2: 合計行数と、フィールド数のばらつき
Top Nの上位だけが合っていても、下位の取りこぼしや壊れた行は見えません。そこで、集計の総和と行数を照合します。
awk '{n++} END{print n}' access.log
wc -l < access.log7
7どちらも7行です。wc -l の出力に空白が付くのはmacOSの仕様で、数値は同じです。ここで差が出たときは、末尾に改行のない最終行や、空行の混入を疑います。
もう1つ、フィールド数のばらつきも見ておくと安全です。
awk '{print NF}' access.log | sort | uniq -c 7 10全7行が10フィールドで揃っています。ここが1種類でなければ、$1 の意味が行によって変わっている可能性があります。ばらつきがあるなら、集計の前に、どの行が違うのかをClaude Codeに洗い出させます。
区切り文字が合わないケース
アクセスログではなくCSVを扱うと、区切り文字の確認が一段と重要です。カンマ区切りのつもりで -F, と指定しても、引用符で囲まれた値の中にカンマがあるとフィールドが増えます。
printf 'a,b,"x,y",c\n' | awk -F, '{print NF}'5見た目は4項目の行が、awkには5フィールドと数えられました。引用符つきCSVは、-F, では正しく割れません。この場合は、CSVを扱える別の道具に切り替える、もしくは引用符を含む行がないことを先に確かめる、のどちらかになります。
Claude Codeには、行ごとの NF の分布を出させるのが近道です。分布が1種類でなければ、その時点で -F, を前提にした集計をやめ、別の手段を選び直す判断ができます。
ここで、リクエストのパス別に数えたいときも考え方は同じです。ダブルクォートで区切って2番目のフィールドを取り出し、さらに空白で分けます。
awk -F'"' '{split($2,r," "); print r[2]}' access.log \
| sort | uniq -c | sort -rn | head -n 2 4 /index.html
2 /a.css-F'"' で区切ると、$2 がリクエスト行全体(メソッド、パス、プロトコル)になります。これをさらに split で分けるのが定石です。
繰り返し使うなら、規約をCLAUDE.mdに置く
集計のたびに同じ指示を書くのは手間です。検算の約束を、プロジェクトのCLAUDE.mdに短くまとめておくと、毎回の指示を減らせます。次は書き方の一例です。
## ログ集計のルール
- awk を書く前に head -n 5 で区切り文字とフィールド数を確認する
- 集計結果は sort | uniq -c など別の方法でも出して突き合わせる
- 総件数は wc -l と照合する。差があれば報告し、原因を調べる
- 入力ファイルは書き換えない。出力は標準出力のみこう書いておけば、Claude Codeは集計の前に確認し、後に検算するという順序を、依頼のたびに守りやすくなります。ただし、CLAUDE.mdの記述は絶対の強制ではありません。守らせたい操作は、権限設定の側でも絞っておきます。
awkは許可リストに入れず、都度確認する
権限ドキュメントには、プロンプトなしで動くBashの読み取り専用コマンドの一覧があります。載っているのは ls、cat、echo、pwd、head、tail、grep、find、wc などで、awk と uniq は並んでいません。許可を書かなければ、awkを実行するたびに確認が出る可能性があります。
確認を減らしたくて Bash(awk *) を許可に入れたくなりますが、ここは慎重に見ます。許可は Bash(npm run *) のようにコマンドの先頭部分とワイルドカードで書きます。Bash(awk *) はawkに渡す引数をすべて通すので、awkのプログラムの中にある print > "file" によるファイル書き込みも、system() によるコマンド実行も、確認なしで通ります。読み取り専用のつもりで許可したのに、書き込みと実行まで許したことになります。
そのため、この記事の運用では次の形に寄せます。
- awkは許可リストに入れない。実行のたびに、表示されたawkプログラムに
>やsystem(が含まれていないかを見てから承認する - 許可に入れるなら、awkプログラム経由の書き込みと実行も通ることを前提に置き、入力ログを壊してよい環境に限る
また、sort や sed のように書き込み系のフラグを持つコマンドは、引用符なしのグロブが付くと確認が出る、とも書かれています。ログ名にワイルドカードを使う場合は、ファイル名を明示するか、確認が出ても承認する前提で進めます。
巨大ログは、先頭で試してから本番の量へ
数GBのログを最初から流すのは、時間も、確認の手間も重くなります。次の順で進めると、手戻りが減ります。
head -n 1000で切り出した小さなファイルで、awkのプログラムと検算を通す- 検算が揃ったら、同じプログラムを全件に流す
- 全件の結果でも、行数の照合とフィールド数の分布を確認する
Claude Codeにログそのものを読ませる場合は、非対話モードの上限にも注意が必要です。claude -p にパイプで渡す標準入力は10MBが上限です。超えると、エラーを出して非ゼロで終了します。大きな入力は、ファイルに書き出してパスをプロンプトで指す方法が示されています。
awk '{c[$1]++} END{for(k in c) print c[k], k}' access.log \
| sort -rn | head -n 20 \
| claude -p "このIP別件数を、上位3件に絞って要約して"この形は、集計は手元のawkで済ませ、Claude Codeには要約された結果だけを渡すものです。全ログではなく、数十行の集計結果だけを渡すので、10MBの上限にも当たりません。共通ワークフローの節にも、git log --oneline -20 | claude -p "summarize these recent commits" という、標準入力を渡す使い方が載っています。
よくあるつまずき
- ヘッダー行を数えている: CSVの先頭行が集計に混ざる。
NR > 1を条件に付けるか、head -n 5の段階でヘッダーの有無を確認する - 空行・壊れた行がある: 総件数と
wc -lが合わない。NFの分布で見つける - awkの実装による差: 手元のmacOSのawkは
awk version 20200816でした。GNU awkにしかない関数をClaude Codeが使うと、環境によって動かない。使う環境を最初に伝える - 同数の順位: Top Nの境界で件数が同じ値が並ぶと、どれが出るかが固定されない。必要なら第2キーで並べ替える
同じように「出力を人が検算できる形で受け取る」進め方は、straceのシステムコール集計を読む手順や、Puppeteerのスクレイピングで件数を検算する手順にも通じます。
まとめ
awkのTop N集計は、コードの短さに反して、区切り文字の誤読に気づきにくい作業です。先頭数行で区切りを確かめ、sort | uniq -c と行数で結果を裏づけるという3つの検算を、Claude Codeへの指示とCLAUDE.mdに組み込めば、数字の根拠を残したまま任せられます。権限は読み取り中心に絞ります。awkは読み取り専用コマンドの一覧になく、許可すればプログラム経由の書き込みと実行まで通ってしまうので、許可リストには入れず、実行のたびにプログラムの中身を見て承認します。
参照した一次ソース
この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。