Linux: パイプとフィルター — Unix哲学の真髄

Unix哲学の核心は「一つのことをうまくやる」です。パイプ|はこれらの小さなプログラムを連鎖させ — 一つの出力が次の入力になります。

📋 前提条件:まず以下を完了していること

1. このレッスンで学ぶこと


2. 10GBのログファイルストーリー

(1) 悩み:ログが大きすぎて手動閲覧不可能

小明は10GBのNginxアクセスログからアクセス数トップ10のIPを見つける必要がありました。エディタで開くのは不可能(10GBは開けない)で、Pythonスクリプトを書くのは大げさでした。

(2) ワンライナーパイプラインの力

ボブがパイプでコマンドを組み合わせる方法を教えてくれました:

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

ℹ️ 注意cat file | commandは一般的ですが、ここでのcatはファイルの内容をパイプに渡すだけ — command < fileまたはawk '{...}' fileを直接使う方が効率的で、プロセスを1つ節約できます。これは「UUOC」(Useless Use of Cat)と呼ばれ、動作はしますがパフォーマンスの最適化が可能です。

出力:

TEXT
 45231 192.168.1.100
 38902 10.0.0.45
 21045 192.168.1.200

(3) 得られるもの:1コマンドで複雑なタスク

小明はパイプラインの組み合わせでほぼあらゆるデータ抽出タスクを処理できることを発見しました — カウント、フィルタリング、並び替え、ランキング、重複削除。1つのコマンドがミニデータ処理パイプラインなのです。


3. 知識ポイント

(1) パイプの仕組み

TEXT

command1 | command2 | command3
    │           │           │
    │   output   │   output   │
    └──────────→│──────────→│──────────→ Final output
                │           │
           command1's stdout connects to command2's stdin
💡 ヒント:パイプは前のコマンドのstdoutを次のコマンドのstdinに接続して動作します — データはカーネルバッファを流れ、ディスクに書き込まれません。つまりパイプライン内のコマンドは並列実行され、大きなファイルを処理してもメモリ不足になりません。

(2) よく使うフィルター

コマンド 用途 よく使うオプション
sort 並び替え -n数値、-r逆順、-k列指定、-u並び替え+重複削除
uniq 重複削除(隣接行) -cカウント、-d重複のみ表示、-u一意のみ表示
wc カウント -l行数、-w単語数、-c文字数
head 先頭表示 -n 10最初の10行、-c 100最初の100文字
tail 末尾表示 -n 10最後の10行、-fリアルタイム追跡
tee ストリーム分割 ファイルと画面の両方に出力
💡 ヒントteeは配管の「T字継手」に由来 — パイプラインデータを2つに分け、一方は下流に渡し、もう一方をファイルに書き込みます。パイプラインのデバッグ時に中間にtee /tmp/debug.txtを挿入すると、最終出力に影響を与えずに中間結果をキャプチャできます。 | nl | 行番号付与 | -ba全行に番号付与 |

(3) ▶ サンプル:ファイル情報のカウント

BASH
# Count number of files
ls -la | wc -l
# Note: wc -l includes the "total" line, so actual file count is one less

# Count total lines in all .md files
cat *.md | wc -l

# Show disk usage of current directory
ls -lh | tail -n +2 | awk '{print $5, $9}'

出力:

TEXT
15
342
4.0K config.yml
12M data.tar.gz

(4) ▶ サンプル:並び替えと重複削除

TEXT
# Sort IP addresses (alphabetical vs numeric)
cat << EOF | sort
192.168.1.100
10.0.0.45
192.168.1.200
10.0.0.2
EOF

# sort -n for numeric sort (by IP segment)
cat << EOF | sort -t. -k1,1n -k2,2n -k3,3n -k4,4n
192.168.1.100
10.0.0.45
192.168.1.200
10.0.0.2
EOF

# Deduplicate and count
cat << EOF | sort | uniq -c | sort -rn
apple
banana
apple
orange
banana
apple
EOF
#       3 apple
#       2 banana
#       1 orange

出力:

TEXT
10.0.0.2
10.0.0.45
192.168.1.100
192.168.1.200
      3 apple
      2 banana
      1 orange

(5) ▶ サンプル:headとtail

BASH
# View first 5 lines
ls -la /etc | head -5

# View last 5 lines
ls -la /etc | tail -5

# Show lines 10-20
cat longfile.txt | head -20 | tail -11

# Follow a log in real-time (press Ctrl+C to exit)
tail -f /var/log/syslog

# View the last 10 log entries
tail -f -n 10 /var/log/syslog

出力:

TEXT
total 72
drwxr-xr-x   2 root root 4096 Jul  7 10:00 bin
drwxr-xr-x   3 root root 4096 Jul  7 10:00 lib
drwxrwxrwt  15 root root 4096 Jul  7 10:30 tmp
BASH
# See results on screen and save to file simultaneously
ls -la | tee output.txt

# Append mode
echo "New content" | tee -a output.txt

# Save intermediate pipeline results
ps aux | tee processes.txt | grep nginx
# First save all processes with tee, then filter for nginx

出力:

TEXT
total 72
drwxr-xr-x   2 root root 4096 Jul  7 10:00 bin
...
New content
root      1234  0.0  0.1  nginx: master

(6) ▶ サンプル:実践的パイプライン組み合わせ

BASH
# 5 largest files
ls -lhS | head -6

# Number of running shells
ps aux | grep bash | wc -l

# Partitions with disk usage over 80%
df -h | grep -v "tmpfs" | awk '{print $5, $6}' | sort -rn

# Find log files in /var/log modified within 7 days
find /var/log -name "*.log" -mtime -7 | sort

# Get external IP
curl -s ifconfig.me | tee my-ip.txt

出力:

TEXT
-r--r--r-- 1 root root  12K config.pkt
-rw-r--r-- 1 root root 8.9M data.tar.gz
3
203.0.113.42

(7) ▶ 総合例:ログ分析パイプラインチェーン

BASH
# Simulate an Nginx access log
cat << 'EOF' > /tmp/access.log
192.168.1.100 - - [07/Jul/2026:10:15:30 +0000] "GET /index.html HTTP/1.1" 200 1234
10.0.0.45 - - [07/Jul/2026:10:16:30 +0000] "GET /api/users HTTP/1.1" 200 5678
192.168.1.100 - - [07/Jul/2026:10:17:30 +0000] "POST /api/login HTTP/1.1" 401 234
10.0.0.45 - - [07/Jul/2026:10:18:30 +0000] "GET /api/users HTTP/1.1" 200 5678
192.168.1.200 - - [07/Jul/2026:10:19:30 +0000] "GET /index.html HTTP/1.1" 404 123
10.0.0.45 - - [07/Jul/2026:10:20:30 +0000] "GET /api/users HTTP/1.1" 200 5678
EOF

echo "=== Total Requests ==="
wc -l /tmp/access.log

echo ""
echo "=== Access Count per IP (Top 5) ==="
cat /tmp/access.log | awk '{print $1}' | sort | uniq -c | sort -rn

echo ""
echo "=== Count per HTTP Status Code ==="
cat /tmp/access.log | awk '{print $9}' | sort | uniq -c | sort -rn

echo ""
echo "=== Most Accessed URLs ==="
cat /tmp/access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -5

echo ""
echo "=== 404 Error Requests ==="
grep " 404 " /tmp/access.log | awk '{print $1, $7}'

❓ よくある質問

Q: パイプラインは直列処理ですか、並列処理ですか? A: \1

Q: パイプとリダイレクトは同時に使えますか? A: はい。例えばcommand > file 2>&1(リダイレクト)とcommand | grep "error"(パイプ)は組み合わせ可能:command 2>&1 | grep "error"

Q: パイプライン途中でエラーが発生すると最終結果に影響しますか? A: はい。パイプライン内のコマンドが失敗すると、下流のコマンドは不完全なデータを受け取る可能性があります。スクリプト内でset -o pipefailを使用すると、パイプライン内のいずれかのコマンドの失敗でパイプライン全体が失敗扱いになります。

Q: tail -fの終了方法は? A: Ctrl + Cを押します。tail -fはファイルの新規内容を継続的に監視し、手動で中断するまで続きます。

Q: 大きなファイルのパイプライン処理でメモリ不足になりますか? A: \1


📖 まとめ


📝 練習問題

  1. 基本(難易度 ⭐)ls -la | wc -lでファイル数をカウントし、/var/log/syslogまたはテストファイルから「error」を含む行を抽出してカウントする
  2. 中級(難易度 ⭐⭐)df -h | grep "^/" | sort -k5 -rnでディスク使用率が最も高いパーティションを見つけ、teeps auxの出力をファイルに保存しながらターミナルにも表示する
  3. 上級(難易度 ⭐⭐⭐):3つ以上のパイプラインコマンドを組み合わせてデータ処理 — システムのメモリ消費上位5プロセスを見つけ、メモリ使用量の降順で並べ、「PID コマンド メモリ%」の形式で出力する
Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%