Linux: 管道与过滤器——Unix 哲学的精髓

Unix 哲学的核心是"一个程序只做一件事,做好它"。管道 | 将这些小程序串联起来——前一个程序的输出就是后一个程序的输入。

📋 前置知识:需要先掌握以下内容

1. 你将学到


2. 一个 10GB 日志文件的故事

(1) 痛点:日志太大,没法手动翻

小明需要从 10GB 的 Nginx 访问日志中找到访问量最高的前 10 个 IP。用编辑器打开不可能(10GB 打不开),用 Python 写脚本又太麻烦。

(2) 一行管道的威力

Bob 教他用管道组合:

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

ℹ️ 说明cat file | command 虽然常见,但其实 cat 在这里只是把文件内容传给管道——直接用 command < fileawk '{...}' file 更高效,省去了一个进程。这被称为"UUOC"(Useless Use of Cat),虽然功能上没问题,但在追求性能时可以优化。

输出:

TEXT 📖 仅展示
 45231 192.168.1.100
 38902 10.0.0.45
 21045 192.168.1.200

(3) 收益:复杂任务一行命令

小明发现管道组合几乎可以完成所有数据提取任务——统计、过滤、排序、排名、去重。一条命令就是一个小型数据处理流水线。


3. 知识点讲解

(1) 管道的工作原理

TEXT 📖 仅展示

command1 | command2 | command3
    │           │           │
    │   输出     │   输出     │
    └──────────→│──────────→│──────────→ 最终输出
                │           │
           command1 的 stdout 连接到 command2 的 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 型接头"——它将管道中的数据一分为二,一份继续往下传,一份写入文件。调试管道时,在中间插入 tee /tmp/debug.txt 可以捕获中间结果而不影响最终输出。 | nl | 加行号 | -ba 所有行都编号 |

▶ 示例:统计文件信息

BASH
# 统计文件数量
ls -la | wc -l
# 注意:wc -l 会包含 "total" 那一行,所以实际文件数要减 1

# 统计所有 .md 文件的总行数
cat *.md | wc -l

# 统计当前目录的磁盘占用
ls -lh | tail -n +2 | awk '{print $5, $9}'

输出:

TEXT 📖 仅展示
15
342
4.0K config.yml
12M data.tar.gz

▶ 示例:排序与去重

TEXT 📖 仅展示
# 对 IP 地址排序(字母序和数字序的区别)
cat << EOF | sort
192.168.1.100
10.0.0.45
192.168.1.200
10.0.0.2
EOF

# sort -n 按数字排序(按 IP 段)
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

# 去重并计数
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

▶ 示例:head 和 tail

BASH
# 查看前 5 行
ls -la /etc | head -5

# 查看后 5 行
ls -la /etc | tail -5

# 显示第 10-20 行
cat longfile.txt | head -20 | tail -11

# 实时追踪日志(按 Ctrl+C 退出)
tail -f /var/log/syslog

# 查看最近 10 条日志
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
# 既在屏幕上看到结果,又保存到文件
ls -la | tee output.txt

# 追加模式
echo "新的内容" | tee -a output.txt

# 将管道中间结果保存
ps aux | tee processes.txt | grep nginx
# 先用 tee 保存所有进程到文件,再过滤出 nginx

输出:

TEXT 📖 仅展示
total 72
drwxr-xr-x   2 root root 4096 Jul  7 10:00 bin
...
新的内容
root      1234  0.0  0.1  nginx: master

▶ 示例:实用管道组合

BASH
# 最大的 5 个文件
ls -lhS | head -6

# 当前运行的 shell 数量
ps aux | grep bash | wc -l

# 查看磁盘使用率超过 80% 的分区
df -h | grep -v "tmpfs" | awk '{print $5, $6}' | sort -rn

# 找出 /var/log 中 7 天内修改过的日志文件
find /var/log -name "*.log" -mtime -7 | sort

# 获取外网 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

▶ 综合示例:日志分析管道链

BASH
# 模拟一个 Nginx 访问日志
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 "=== 总请求数 ==="
wc -l /tmp/access.log

echo ""
echo "=== 每个 IP 的访问次数(Top 5)==="
cat /tmp/access.log | awk '{print $1}' | sort | uniq -c | sort -rn

echo ""
echo "=== 每个 HTTP 状态码的数量 ==="
cat /tmp/access.log | awk '{print $9}' | sort | uniq -c | sort -rn

echo ""
echo "=== 访问最多的 URL ==="
cat /tmp/access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -5

echo ""
echo "=== 404 错误的请求 ==="
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 + Ctail -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%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏