1-7. Level 1 総復習 - ログ解析ワンライナーで卒業判定

所要時間: 40-60分(実践重視。2セッション分) ゴール: Day1-6 の道具を組み合わせて、現場と同じ「ログ集計」を自力で書ける コミット内容: 今日書いたワンライナー集を ~/log/linux_day07_oneliners.md に保存

この章が終わるとできること

  • Day 1-6 で習ったコマンドの 「何のためか」 を即答できる
  • アクセスログから「上位 IP / 上位 URL / 5xx だけ」を 自力でワンライナー で抽出できる
  • 自分が書いたパイプラインを「他人に説明できる」(各ステップ何をしているか)
  • 詰まったら どの章に戻ればいいか を即判断できる(自己ナビゲーション力)

これができると何が嬉しいか

  • Linux で困らない人」として Level 2(シェルスクリプト)に進める自信が付く
  • 本番障害対応で「とりあえずログを見る」が10分以内に終わるベース技能が揃う
  • ここでスキマ(自分が忘れてる章)が見えるので、Level 2 前の補強ができる

大前提: Level 1 で何を身に着けたか

ここまでの6日間で、以下を学んだ:

Day主題主な道具
Day1ターミナル基礎pwd, ls, cd, cat, less, head, tail, tree, mkdir, touch, man
Day2ファイル操作cp, mv, rm, find, ln
Day3パーミッションchmod, chown, chgrp, sudo, umask, /etc/passwd, /etc/group
Day4テキスト処理grep, awk, sed, sort, uniq, wc
Day5パイプ・リダイレクト>, >>, 2>, 2>&1, &>, |, tee, xargs, <<EOF
Day6プロセス制御ps, top, kill, ジョブ制御, nohup, lsof

これらは 「サーバーの中を見て、ファイルを操って、テキストを料理する」 ためのコア能力。今日はこれを 本物の課題 で使えるか試す。


大前提: なぜログ解析が「卒業課題」なのか

バックエンドエンジニアの日常タスクで頻度が圧倒的に高いもの

  1. 障害対応: 「APIが500を返してる」 → アクセスログ・エラーログを掘る
  2. パフォーマンス調査: 「特定APIが遅い」 → リクエスト時間でソート
  3. ユーザー分析: 「どの機能がよく使われてる?」 → ログから利用頻度を集計
  4. セキュリティ調査: 「不審なアクセスがある?」 → 失敗ログイン回数をIP別に

これらすべて grep | awk | sort | uniq | head パターン で解ける。逆に言えば、このパターンが書けないとバックエンドの現場で詰まる。

今日の課題をクリアできれば、「Linuxの基本操作で困らない人」として Level 2(シェルスクリプト)に進める。


セッション①: 知識の再確認(20-25分)

0. 録画スタート&作業ディレクトリ

mkdir -p ~/log ~/learn/linux/day07
cd ~/learn/linux/day07
script ~/log/linux_day07.log

1. クイック復習: 「これ何のコマンドだっけ?」

即答できますか?

以下を見て、何のコマンドか・何のオプションか思い出してみる。答えは下のCalloutに

  1. ls -lah
  2. mkdir -p
  3. cp -a
  4. rm -rf
  5. find . -name "*.log" -mtime +30 -delete
  6. chmod 600 ~/.ssh/id_ed25519
  7. chown -R www-data:www-data /var/www
  8. grep -rn "TODO" src/
  9. awk '{print $3}' file | sort | uniq -c | sort -rn
  10. sed -i.bak 's/old/new/g' file
  11. command 2>/dev/null
  12. nohup ./task.sh > task.log 2>&1 &
  13. lsof -i :3000
  14. kill -9 <PID>
  15. ps aux | grep nginx | grep -v grep

答え合わせ

  1. ファイル一覧(長形式・隠しファイル込み・読みやすいサイズ)
  2. 親ディレクトリも含めて再帰的に作成。既存でもエラーにならない
  3. ディレクトリを「アーカイブ」モードでコピー(権限・タイムスタンプ・リンク維持)
  4. ディレクトリ含めて強制削除。最も危険なコマンド
  5. 30日以上前の .log ファイルを削除(cron での定期実行に使う)
  6. 秘密鍵を「所有者だけ読み書き可」に。SSH ログイン必須要件
  7. /var/www 配下の所有者を www-data に再帰変更(Webサーバーが読み書きできるように)
  8. src 配下を再帰検索して TODO を含む行を行番号付きで表示
  9. 3列目の出現頻度ランキング(ログ集計の鉄板パターン
  10. ファイルを直接編集(.bak でバックアップ作成)
  11. エラー出力を捨てる
  12. ターミナル切断後も動き続けるバックグラウンドタスク起動
  13. ポート3000 を掴んでいるプロセスを特定
  14. 強制終了(最終手段)
  15. nginx プロセスを検索(grep自身を除外)

2. 各分野で「これだけは押さえる」5項目

ファイル操作

  • ls -lah で常に詳細確認
  • cp -r でディレクトリコピー、-a で属性保持
  • mv は「移動」と「リネーム」が同じ操作
  • rm -rf の前は必ず pwdls で対象確認
  • find . -name "..." でファイル検索 → -execxargs で一括処理

パーミッション

  • 数値は r=4, w=2, x=1 で足す(755 = rwx r-x r-x
  • chmod 755 = 実行ファイル/ディレクトリ、chmod 644 = 通常ファイル、chmod 600 = 秘密鍵
  • ~/.ssh700.ssh/id_*600
  • chmod 777 は最終手段、安易に使わない
  • sudo は「rootになる」のではなく「単発でroot権限を借りる」

テキスト処理

  • grep -inrE の組み合わせを覚える(行番号 + 大小無視 + 再帰 + 拡張正規表現)
  • awk '{print $N}' で N 列目を取り出す
  • sed 's/old/new/g' で置換
  • sort | uniq -c | sort -rn集計の最強パターン
  • wc -l で行数カウント

パイプ・リダイレクト

  • stdin / stdout / stderr が分かれている前提を持つ
  • 2>/dev/null でエラー抑制
  • 2>&1 で stderr を stdout にマージ
  • tee で「画面表示と保存」を両立
  • xargs で stdin を「引数」に変換(-print0 | xargs -0 でスペース対応)

プロセス

  • ps aux | grep <name> | grep -v grep で検索
  • top / htop でリアルタイム監視
  • kill は SIGTERM → ダメなら SIGKILL の順番
  • Ctrl+C (SIGINT) / Ctrl+Z (SIGTSTP) / bg / fg でジョブ制御
  • lsof -i :<port> でポート占有プロセス特定

セッション②: 実践課題 - ログ解析ワンライナー(25-35分)

3. 課題ログを用意

下のスクリプトは「コピペで動かすだけ」でOK

中身では Level 2 で扱うシェルスクリプト構文(配列 arr=(...)for ... do ... done ループ、変数代入と ${arr[index]} 参照、$RANDOM$(...) コマンド置換、$(( ... )) 算術展開、printfseq)を一気に使っている。 いまの段階で全部理解する必要はなく、生成された access.log を後段の集計課題で使うのが目的。各構文の文法は Linux 2-2 / 2-3 で詳しく扱う。

# 本番の access.log を模した1000行のログを生成
# ↓ これ以下のヒアドキュメント (<<'EOF' ... EOF) は Linux 1-5 で扱った構文。
#   ファイル gen_log.sh に複数行のスクリプトをまるごと書き込んでいる。
cat > ~/learn/linux/day07/gen_log.sh <<'EOF'
#!/bin/bash
# シンプルなアクセスログ生成器
# 以下の `名前=(...)` は「配列」と呼ばれる Level 2 範囲の構文。「複数の値を持つ変数」のこと。
IPS=("192.168.1.10" "192.168.1.11" "192.168.1.12" "10.0.0.5" "10.0.0.6" "172.16.0.20" "203.0.113.42" "198.51.100.7")
METHODS=("GET" "GET" "GET" "GET" "POST" "POST" "PUT" "DELETE")
PATHS=("/api/users" "/api/users/123" "/api/products" "/api/orders" "/api/login" "/api/logout" "/api/cart" "/health")
STATUSES=("200" "200" "200" "200" "200" "201" "301" "400" "401" "403" "404" "500" "502" "503")
USER_AGENTS=("Mozilla/5.0" "curl/7.79" "PostmanRuntime/7.28" "axios/0.27" "Python/requests")
 
# `for x in リスト; do ... done` は「リストの要素を1個ずつ x に入れて繰り返す」ループ構文(Linux 2-3)。
# `$(seq 1 1000)` は seq コマンドで「1から1000までの数字」を生成し、コマンド置換でリスト化したもの。
# `$RANDOM` は bash の特殊変数で「0〜32767 のランダムな整数」が毎回入る。
# `$(( 式 ))` は「整数の計算をする」算術展開。`$(( RANDOM % 60 ))` は 0〜59 のランダム値になる。
# `${IPS[index]}` は配列のインデックス参照、`${#IPS[@]}` は配列の要素数。
for i in $(seq 1 1000); do
    TIME=$(printf "10:%02d:%02d" $((RANDOM % 60)) $((RANDOM % 60)))
    IP=${IPS[$RANDOM % ${#IPS[@]}]}
    METHOD=${METHODS[$RANDOM % ${#METHODS[@]}]}
    PATH_VAL=${PATHS[$RANDOM % ${#PATHS[@]}]}
    STATUS=${STATUSES[$RANDOM % ${#STATUSES[@]}]}
    SIZE=$((RANDOM % 50000 + 100))
    UA=${USER_AGENTS[$RANDOM % ${#USER_AGENTS[@]}]}
    LATENCY=$((RANDOM % 2000 + 10))
    echo "2026-05-20 $TIME $IP $METHOD $PATH_VAL $STATUS $SIZE \"$UA\" ${LATENCY}ms"
done
EOF
 
chmod +x ~/learn/linux/day07/gen_log.sh
~/learn/linux/day07/gen_log.sh > access.log
 
# 確認
head -3 access.log
wc -l access.log    # 1000 と出るはず

サンプル行:

2026-05-20 10:23:45 192.168.1.10 GET /api/users 200 12345 "Mozilla/5.0" 234ms

カラム構造:

$1=日付  $2=時刻  $3=IP  $4=メソッド  $5=パス  $6=ステータス  $7=サイズ  $8=UA(クォート付)  $9=レイテンシ

4. 課題セット A: 基礎集計

課題 A-1

このログには全部で何行ありますか?

wc -l access.log

課題 A-2

ステータスコード 500 の行が何件ありますか?

# 方法1: grep -c
grep -c " 500 " access.log
 
# 方法2: awk
awk '$6 == 500' access.log | wc -l

grep "500" だけだと罠

「500」という3文字はIPやサイズやレイテンシにも現れる。ステータスコードの位置で絞らないと誤検出する

  • 安全: awk '$6 == 500'
  • 妥協: grep " 500 "(前後にスペースを入れて単独ステータスっぽくする)

課題 A-3

アクセス数の多いIPトップ5を出してください。

awk '{print $3}' access.log | sort | uniq -c | sort -rn | head -5

出力イメージ

     138 192.168.1.10
     127 10.0.0.5
     125 172.16.0.20
     123 203.0.113.42
     121 192.168.1.11

このパターンの再利用

awk '{print $N}' file | sort | uniq -c | sort -rn | head「N列目のランキング」を作る万能ワンライナー

「N」を変えるだけで:

  • $3 → IPランキング
  • $4 → HTTPメソッドランキング
  • $5 → エンドポイントランキング
  • $6 → ステータスコードランキング

5. 課題セット B: 条件付き集計

課題 B-1

500エラーを起こしたパス(エンドポイント)の多い順トップ3。

awk '$6 == 500 {print $5}' access.log | sort | uniq -c | sort -rn | head -3

課題 B-2

POST リクエストだけに絞って、ステータスコードの分布を出す。

awk '$4 == "POST" {print $6}' access.log | sort | uniq -c | sort -rn

課題 B-3

平均レイテンシ(9列目から ms を除いた数値)。

awk '{
    gsub("ms", "", $9);   # "234ms" → "234"
    sum += $9; count++
}
END { print sum / count " ms" }' access.log

awk の END ブロック

awk は { ... } を各行に対して実行するが、END { ... }全行処理が終わった後 に1回だけ実行される。集計(合計、平均、最大値など)の最終出力に使う。

同様に BEGIN { ... }最初に1回だけ実行されるブロック(ヘッダ出力などに使う)。

6. 課題セット C: 複合パターン

課題 C-1

「5xx エラーを起こしたIP」を多い順に、その回数とともに表示。

awk '$6 ~ /^5[0-9]{2}$/ {print $3}' access.log | sort | uniq -c | sort -rn

awk の ~ (正規表現マッチ)演算子

$6 ~ /pattern/ は「$6がpatternにマッチするか」をbool値で返す。!~ で否定。

例:

  • $6 ~ /^[45]/ → 4xx または 5xx のステータス
  • $5 !~ /health/ → /healthcheckなど除外
  • $0 ~ /ERROR/ → 行全体に “ERROR” を含む

課題 C-2

/api/users 系のエンドポイントへのアクセス」が時間帯別(時単位)でどう推移したか。

awk '$5 ~ /^\/api\/users/ {print substr($2, 1, 2)}' access.log | sort | uniq -c

substr で文字列の一部を取る

awk の substr(文字列, 開始位置, 長さ) は1文字目から始まる(C言語と違って0ではなく1)。 substr("10:23:45", 1, 2)"10" (時の部分)

課題 C-3

「エラー(4xx か 5xx)を起こしたパス」を集計して、CSV形式(パス,件数)で error_report.csv に保存。

awk '$6 ~ /^[45]/ {print $5}' access.log | sort | uniq -c | sort -rn | \
  awk '{printf "%s,%d\n", $2, $1}' > error_report.csv
 
cat error_report.csv

7. 課題セット D: 究極ワンライナー

課題 D-1(卒業試験)

以下をすべて満たすレポートを1つのコマンドで:

  1. 5xxエラーを起こした行だけを対象に
  2. パスごとに件数を数え
  3. 多い順にソートし
  4. 上位3件を画面に表示しつつ
  5. 同じ内容を top_errors.log にも保存
awk '$6 ~ /^5/ {print $5}' access.log | sort | uniq -c | sort -rn | head -3 | tee top_errors.log

これが書ければ Level 1 卒業。

解説

このワンライナーには Level 1 全部が詰まっている:

  • awk: 条件付き列抽出(Day4)
  • sort | uniq -c: 集計パターン(Day4)
  • head -3: 上位N件取得(Day1)
  • | : パイプ連結(Day5)
  • tee: 画面表示+ファイル保存(Day5)

これを暗記する必要はない。「やりたいこと → 道具の組み合わせ」を組み立てる発想が身につけばOK。

8. 応用課題(時間があれば)

応用 1

レイテンシ(応答時間)が500msを超えたリクエストの数を、パス別ランキングで出す。

awk '{
    gsub("ms", "", $9);
    if ($9 + 0 > 500) print $5
}' access.log | sort | uniq -c | sort -rn | head

応用 2

失敗ログイン(POST /api/login で 401 を返した)が3回以上ある不審なIPを抽出。

awk '$4 == "POST" && $5 == "/api/login" && $6 == 401 {print $3}' access.log | \
  sort | uniq -c | awk '$1 >= 3 {print $0}'

応用 3

アクセス頻度トップIPを bar chart 風に視覚化(# の数で表現)。

# ↓ 2つ目の awk 内で使っている `for (i = 0; i < N; i++)` は awk 内蔵の繰り返し構文(C 言語風)。
#   1つ目の awk が出力した「件数 IPアドレス」の各行に対して、件数を 5 で割った数だけ "#" を文字列に足して、棒グラフ風に表示している。
#   awk スクリプト内の制御構文は Linux 2-3 / awk 発展で詳しく扱う。ここではコピペして出力を眺めればOK。
awk '{print $3}' access.log | sort | uniq -c | sort -rn | head -5 | \
  awk '{
    bar = ""
    for (i = 0; i < $1 / 5; i++) bar = bar "#"
    printf "%-20s %s (%d)\n", $2, bar, $1
  }'

締め: 振り返り&コミット(10分)

1. ワンライナー集を保存

cat > ~/log/linux_day07_oneliners.md <<'EOF'
# 自分専用ワンライナー集
 
## ログ集計の基本
awk '{print $3}' access.log | sort | uniq -c | sort -rn | head
 
## 5xxエラーのパスランキング
awk '$6 ~ /^5/ {print $5}' access.log | sort | uniq -c | sort -rn | head
 
## 平均レイテンシ
awk '{gsub("ms","",$9); sum += $9; count++} END {print sum/count " ms"}' access.log
 
## ポート占有プロセスを特定して kill
lsof -i :3000 | awk 'NR>1 {print $2}' | xargs kill
 
## 大きいファイル探し(1MB以上)
find . -type f -size +1M -exec ls -lh {} \; | sort -k5 -hr
 
## 30日以上前のログを削除
find /var/log -name "*.log" -mtime +30 -delete
 
# 自分で書いて「これ便利」と思ったワンライナーをここに足していく
EOF

2. セッション録画を終了

exit

3. 今日の発見

このノートに追記:

- 課題のうち一番手こずったもの:
- 「これ覚えておきたい」と思ったワンライナー:
- 想像していたよりLinuxでできることが多い、と感じた瞬間:
- Level 2 でやりたいこと:

Level 1 卒業チェックリスト

これが全部できれば Level 1 卒業 → Level 2 へ:

ファイル操作

  • pwd ls -lah cd mkdir -p を無意識に打てる
  • cp -r でディレクトリコピーができる
  • mv でファイルを移動・リネームできる
  • rm -rf の危険性を口頭で説明できる
  • find . -name "*.log" -mtime +30 を書ける
  • シンボリックリンク(ln -s)が何か説明できる

パーミッション

  • chmod 755 / 644 / 600 / 700 の使い分けが分かる
  • ~/.ssh の権限(700 / 600)が必須な理由を説明できる
  • chown で所有者を変更できる
  • sudo の仕組みを「単発昇格」として理解している

テキスト処理

  • grep -inrE で柔軟な検索ができる
  • awk '{print $N}' で列を取り出せる
  • sed 's/x/y/g' で置換できる
  • sort | uniq -c | sort -rn の集計パターンを書ける
  • wc -l で行数カウントできる

パイプ・リダイレクト

  • stdin / stdout / stderr の3つを意識して書ける
  • 2>/dev/null でエラーだけ抑制できる
  • tee で画面とファイル両方に出力できる
  • xargsfind の結果を別コマンドに渡せる
  • ヒアドキュメント <<EOF でファイル生成できる

プロセス

  • ps aux | grep <name> でプロセス検索できる
  • top を起動してメモリ・CPU使用率を見れる
  • kill <PID>kill -9 <PID> を使い分けられる
  • Ctrl+Z bg fg でジョブ制御できる
  • nohup でターミナル切断耐性のあるプロセスを起動できる
  • lsof -i :<port> でポート占有プロセスを特定できる

統合

  • 5段以上のパイプを使った集計ワンライナーを書ける
  • ログから「上位N件」を抽出するパターンが書ける
  • 「やりたいこと」を「コマンドの組み合わせ」に分解する思考ができる

「実務OK」基準(Level 1全体)

ここまで来れば、以下の現場シーンで自走できる:

  • 「アプリが落ちてる、ログ見て」と言われ、tail -f で原因特定ができる
  • 「メモリ食ってるプロセスが居る」と言われ、top -o mem / ps aux --sort=-%mem で特定できる
  • 「ポートが既に使われている」エラーを lsof -i で解決できる
  • 「直近の500エラーを集計してレポート」と言われ、ワンライナーで30秒で出せる
  • 権限エラーを ls -l で診断し、最小限の chmod で解決できる

ここまでが 「Linuxの基本操作で詰まらない人」。本番サーバーに ssh しても怖くない、というレベル。


詰まった時のチートシート(Level 1 完全版)

カテゴリやりたいことコマンド
移動現在地pwd
移動移動cd <path> / cd - / cd ~
閲覧一覧ls -lah
閲覧中身(短)cat <file>
閲覧中身(長)less <file>
閲覧末尾追跡tail -f <file>
閲覧構造tree -L 2
作成フォルダmkdir -p <path>
作成ファイルtouch <file>
操作コピーcp -r <src> <dst>
操作移動mv <src> <dst>
操作削除rm -rf <path>
検索ファイルfind <path> -name "<pattern>"
検索内容grep -rn "<pattern>" <path>
権限確認ls -l <file>
権限変更chmod <755|644|600|700> <path>
権限所有者chown <user>:<group> <path>
テキスト列抽出awk '{print $N}' <file>
テキスト置換sed 's/old/new/g' <file>
テキスト並べ替えsort -n / sort -rn
テキスト集計sort | uniq -c | sort -rn
テキスト行数wc -l <file>
I/O出力保存> <file>
I/O追記>> <file>
I/Oエラー捨て2>/dev/null
I/O両方保存&> <file>
I/O画面+保存| tee <file>
I/O引数化| xargs <cmd>
プロセス一覧ps aux
プロセス監視top / htop
プロセス終了kill <PID> / kill -9 <PID>
プロセスBG起動<cmd> &
プロセス切断耐性nohup <cmd> > log 2>&1 &
プロセスポート占有lsof -i :<port>

最終チャレンジ: CTF

Level 1 で習ったコマンドだけで解ける CTF 形式の演習を別章に用意した。 詳しくは Day 1-5 CTF へ。


Level 1 自己評価チェックリスト

ここまでで自信を持って言えるものに ☑ する。3つ以上 ☐ が残るならその章をもう一度。

  • ls -lah の出力を全列読める(Day 1)
  • pwd を反射的に打つクセが付いた(Day 1)
  • rm -rf の危険を後輩に説明できる(Day 2)
  • find . -name "*.log" -mtime +30 のような複合条件を書ける(Day 2)
  • chmod 755 / 644 / 700 / 600 を「何のため」付きで暗記している(Day 3)
  • Permission denied を見たら反射で ls -l を打つ(Day 3)
  • grep | awk | sort | uniq -c | sort -rn の集計パターンが書ける(Day 4-5)
  • 2>/dev/null &> の意味が即答できる(Day 5)
  • ps aux | grep <name> で対象を見つけ、kill <PID> で止められる(Day 6)
  • lsof -i :3000 でポート占有プロセスを特定できる(Day 6)

次のレッスン: Level 2 へ

ここまで学んだコマンドを「毎回手で打つ」のはもったいない。同じ操作を何度もやるなら シェルスクリプト にまとめて自動化したい。

Level 2 では:

  • シェルスクリプトの基礎(shebang, 変数, if, for)
  • 関数とエラー処理set -e, set -u, exit code)
  • 環境変数と PATH
  • cron で定期実行
  • 実践プロジェクト: バックアップスクリプト、ログローテーション、ヘルスチェック

Day 8: シェルと環境変数


Level 1 完了

ここまで来たあなたは、「ターミナルで作業できるエンジニア」 の入口に立っている。

今までGUI(Finder, テキストエディタ)でしかできなかったことが、コマンドで圧倒的に速くできることを体感したはず。

Level 2 では「単発のコマンド」から「再利用可能なスクリプト」へ進化させる。1つの仕事を100回繰り返す代わりに、10秒のスクリプトを1回書く――そのマインドセットを身につけていく。