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 |
これらは 「サーバーの中を見て、ファイルを操って、テキストを料理する」 ためのコア能力。今日はこれを 本物の課題 で使えるか試す。
大前提: なぜログ解析が「卒業課題」なのか
バックエンドエンジニアの日常タスクで頻度が圧倒的に高いもの:
- 障害対応: 「APIが500を返してる」 → アクセスログ・エラーログを掘る
- パフォーマンス調査: 「特定APIが遅い」 → リクエスト時間でソート
- ユーザー分析: 「どの機能がよく使われてる?」 → ログから利用頻度を集計
- セキュリティ調査: 「不審なアクセスがある?」 → 失敗ログイン回数を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.log1. クイック復習: 「これ何のコマンドだっけ?」
即答できますか?
以下を見て、何のコマンドか・何のオプションか思い出してみる。答えは下のCalloutに。
ls -lahmkdir -pcp -arm -rffind . -name "*.log" -mtime +30 -deletechmod 600 ~/.ssh/id_ed25519chown -R www-data:www-data /var/wwwgrep -rn "TODO" src/awk '{print $3}' file | sort | uniq -c | sort -rnsed -i.bak 's/old/new/g' filecommand 2>/dev/nullnohup ./task.sh > task.log 2>&1 &lsof -i :3000kill -9 <PID>ps aux | grep nginx | grep -v grep
答え合わせ
- ファイル一覧(長形式・隠しファイル込み・読みやすいサイズ)
- 親ディレクトリも含めて再帰的に作成。既存でもエラーにならない
- ディレクトリを「アーカイブ」モードでコピー(権限・タイムスタンプ・リンク維持)
- ディレクトリ含めて強制削除。最も危険なコマンド
- 30日以上前の .log ファイルを削除(cron での定期実行に使う)
- 秘密鍵を「所有者だけ読み書き可」に。SSH ログイン必須要件
- /var/www 配下の所有者を www-data に再帰変更(Webサーバーが読み書きできるように)
- src 配下を再帰検索して TODO を含む行を行番号付きで表示
- 3列目の出現頻度ランキング(ログ集計の鉄板パターン)
- ファイルを直接編集(.bak でバックアップ作成)
- エラー出力を捨てる
- ターミナル切断後も動き続けるバックグラウンドタスク起動
- ポート3000 を掴んでいるプロセスを特定
- 強制終了(最終手段)
- nginx プロセスを検索(grep自身を除外)
2. 各分野で「これだけは押さえる」5項目
ファイル操作
ls -lahで常に詳細確認cp -rでディレクトリコピー、-aで属性保持mvは「移動」と「リネーム」が同じ操作rm -rfの前は必ずpwdとlsで対象確認find . -name "..."でファイル検索 →-execかxargsで一括処理
パーミッション
- 数値は
r=4, w=2, x=1で足す(755 = rwx r-x r-x)chmod 755= 実行ファイル/ディレクトリ、chmod 644= 通常ファイル、chmod 600= 秘密鍵~/.sshは700、.ssh/id_*は600chmod 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、$(...)コマンド置換、$(( ... ))算術展開、printf、seq)を一気に使っている。 いまの段階で全部理解する必要はなく、生成された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.logawk の 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 -rnawk の
~(正規表現マッチ)演算子
$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.csv7. 課題セット D: 究極ワンライナー
課題 D-1(卒業試験)
以下をすべて満たすレポートを1つのコマンドで:
- 5xxエラーを起こした行だけを対象に
- パスごとに件数を数え
- 多い順にソートし
- 上位3件を画面に表示しつつ
- 同じ内容を
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
# 自分で書いて「これ便利」と思ったワンライナーをここに足していく
EOF2. セッション録画を終了
exit3. 今日の発見
このノートに追記:
- 課題のうち一番手こずったもの:
- 「これ覚えておきたい」と思ったワンライナー:
- 想像していたよりLinuxでできることが多い、と感じた瞬間:
- Level 2 でやりたいこと:
Level 1 卒業チェックリスト
これが全部できれば Level 1 卒業 → Level 2 へ:
ファイル操作
-
pwdls -lahcdmkdir -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で画面とファイル両方に出力できる -
xargsでfindの結果を別コマンドに渡せる - ヒアドキュメント
<<EOFでファイル生成できる
プロセス
-
ps aux | grep <name>でプロセス検索できる -
topを起動してメモリ・CPU使用率を見れる -
kill <PID>とkill -9 <PID>を使い分けられる -
Ctrl+Zbgfgでジョブ制御できる -
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回書く――そのマインドセットを身につけていく。