shibomb

grep・sed・awkをパイプで繋ぐ!Linuxログから原因と不審IPを即特定

ログ調査のたびにエディタやGUIツールで開き、検索と目視を繰り返していませんか。ファイルが数百MBになると、開くだけで時間がかかります。この記事では、grep・sed・awk を パイプライン で繋ぎ、ターミナルだけで素早く絞り込み、加工、集計する方法を解説します。最後にアクセスログから「エラーが多いエンドポイント」と「怪しいIP」を数コマンドで洗い出す実践例も紹介します。コピーして試せる形で書いているので、手元のログで動かしてみてください。

なぜエンジニアにLinuxコマンドのパイプライン処理が必要なのか?

パイプライン (|) は、あるコマンドの出力を次のコマンドの入力に渡す仕組みです。UNIX の設計思想では「1つのことをうまくやる小さな道具を組み合わせる」ことが重視されてきました。検索は grep、置換は sed、列の処理は awk と役割を分け、必要に応じて繋ぎます。

この方式には実務上の利点が3つあります。

  1. サーバにログインしただけで、追加ツールなしに調査を始められる
  2. 大きなファイルでも、1行ずつ流れ作業で処理するため、メモリを圧迫しにくい
  3. 実行したコマンドをそのまま手順書やスクリプトにでき、チームで再現できる

GUIが悪いわけではありません。ただ、SSH 先のサーバにはGUIがないことが多く、調査の手順を他人に共有しにくい点が弱点です。コマンドなら、1行を共有するだけで同じ結果を再現できます。

まず押さえたい基本!grepによる高速フィルタリングと正規表現の勘所

grep は「条件に合う行を取り出す」コマンドです。まず覚えたいオプションは次の通りです。

オプション意味
-n行番号を表示
-i大文字小文字を無視
-v一致しない行を表示 (除外)
-c一致した行数を表示
-rディレクトリを再帰的に検索
-E拡張正規表現を使う
-A / -B / -C前後の行も表示
# ERROR または FATAL を含む行を、前後2行つきで表示
grep -nE "ERROR|FATAL" -C 2 app.log

# ヘルスチェックを除外して件数を数える
grep -v "/health" access.log | wc -l

正規表現では ^(行頭)、$(行末)、.(任意の1文字)、*(直前の0回以上の繰り返し)を押さえれば、多くの場面をカバーできます。-E を付けると | や + を、バックスラッシュなしで書けます。

注意点として、-P(Perl互換正規表現)は GNU grep の機能で、macOS 標準の grep など環境によっては使えません。チームで共有するコマンドは、まず -E の範囲で書くと互換性の問題を避けられます。また .gz に圧縮されたログは zgrep で、解凍せずに検索できます。

sedとawkで差をつける!行・列データの抽出と一括テキスト置換

sed は「行単位のストリーム編集」が得意です。代表例は置換 (s) と行の範囲指定です。

# 画面上だけで置換 (ファイルは変更されない)
sed 's/staging/production/g' config.txt

# 10〜20行目だけを表示
sed -n '10,20p' app.log

ファイルを直接書き換える -i は強力ですが、元に戻せません。GNU sed では -i.bak で、バックアップを残せます。macOS (BSD sed) は -i の直後に拡張子の指定が必須で、書き方が異なります。環境差があるため、まずは -i なしで結果を確認する習慣を付けてください。

awk は「空白などで区切られた列」を扱うのが得意です。デフォルトでは空白区切りで、1列目が $1、行全体が $0 です。

# 1列目と3列目だけを表示
awk '{print $1, $3}' access.log

# CSVの2列目が1000より大きい行を表示 (区切り文字は -F で指定)
awk -F, '$2 > 1000 {print $0}' data.csv

# 列ごとの合計を END で出力
awk -F, '{sum += $2} END {print sum}' data.csv

使い分けの目安は、行ごと置換するなら sed、列を見て条件分岐や集計をするなら awk です。awk は連想配列も持つため、後述のように sort | uniq -c なしで集計することもできます。

パイプ・リダイレクト・xargsを組み合わせて複数コマンドを自在に繋ぐ

パイプと合わせて覚えたいのがリダイレクトです。> はファイルへの上書き、>> は追記、2>&1 は標準エラー出力を標準出力にまとめます。

# 結果をファイルに保存しつつ、画面でも確認する
grep "ERROR" app.log | tee errors.txt | wc -l

集計の定番パターンは sort | uniq -c | sort -nr です。uniq は隣り合う同じ行しかまとめないため、先に sort が必要です。これを忘れると、集計結果がずれます。

# 出現回数の多い順に上位10件
cut -d' ' -f1 access.log | sort | uniq -c | sort -nr | head -10

xargs は、標準入力から受け取った文字列を別コマンドの引数に変換します。ファイル名に空白が含まれる場合に備え、find の -print0 と xargs -0 を組み合わせると安全です。

# .log ファイルから "timeout" を含む行を検索
find /var/log/myapp -name "*.log" -print0 | xargs -0 grep -n "timeout"

リアルタイム監視では tail -f app.log | grep --line-buffered "ERROR" のように、--line-buffered を付けます。付けないと出力がバッファに溜まり、表示が遅れることがあります。

実践シナリオ:アクセスログからエラー多発エンドポイントと特定IPを瞬時に抽出する

ここでは、Nginx や Apache で一般的な combined 形式のログを例にします。空白区切りで、1列目がクライアントIP、7列目がリクエストパス、9列目がHTTPステータスです。ログ形式を独自に変更している環境では列の位置がずれるため、まず head -3 access.log で実際の中身を確認してください。

最初に、5xx エラーが多いエンドポイントの上位を出します。

awk '$9 ~ /^5/ {print $7}' access.log \
  | sed 's/?.*//' \
  | sort | uniq -c | sort -nr | head -10

awk で5xx系だけを抽出し、sed でクエリ文字列を除去してパスごとに集約しています。?id=123 のようなパラメータが付いたままだと、同じエンドポイントが別物として数えられるためです。

次に、リクエスト数の多いIPを調べます。

awk '{print $1}' access.log | sort | uniq -c | sort -nr | head -10

気になるIPが見つかったら、そのIPが何をしているかを絞り込みます。ここでは文書用のIPアドレス (203.0.113.5) を使います。

grep '^203.0.113.5 ' access.log | awk '{print $9, $7}' | sort | uniq -c | sort -nr | head

awk だけで集計することも可能です。

awk '$9 >= 500 {c[$7]++} END {for (p in c) print c[p], p}' access.log | sort -nr | head

この調査から「特定のIPがログインページに大量アクセスしている」といった傾向が見えれば、WAF 設定やレート制限の検討材料になります。ただし、リクエスト数が多いだけで攻撃と断定はできません。社内のプロキシや監視ツールを経由したアクセスも、同一IPに集約されるためです。User-Agent や時間帯など他の情報と合わせて判断してください。

サーバ作業で事故を防ぐ!安全にコマンドを実行・検証するためのチェックリスト

パイプラインは強力な分、破壊的な操作にも繋がります。本番サーバで実行する前に、次の点を確認してください。

  1. まず読み取り専用で試す: sed は -i なしで、出力を確認してから反映します
  2. バックアップを取る: -i.bak (GNU sed) や cp file file.bak で、元に戻せる状態にします
  3. xargs は echo で予行演習する: xargs -0 echo rm のように、実行されるコマンドを先に表示します
  4. 出力を絞る: 最初は | head を付け、大量出力で端末やSSH接続が重くなるのを防ぎます
  5. 元のログは直接編集しない: 解析用にコピーして作業します。ログローテーション中のファイルは特に注意が必要です
  6. 列番号と形式を確認する: head で数行を見て、$7 や $9 が想定通りか確かめます
  7. クォートを徹底する: 正規表現や $ を含む式は、シングルクォートで囲みシェルによる展開を防ぎます

さらに、パイプラインを少しずつ伸ばして書く方法も有効です。grep の結果を確認し、次に awk を足し、その結果を見て sort | uniq -c を足します。1段ずつ出力を確かめれば、間違いにすぐ気付けます。完成したコマンドは、コメントを添えてシェルスクリプトや社内Wikiに残すと、チームの調査手順として再利用できます。

関連記事