CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
Shell 与 Bash 入门/课时/管道与文本处理
RawBlame
课时目录12 节

Shell 与 Bash 入门

  1. 01Shell 是什么
  2. 02目录与文件导航
  3. 03文件操作与权限
  4. 04管道与文本处理
  5. 05变量与字符串
  6. 06条件与循环
  7. 07函数与脚本结构
  8. 08正则表达式
  9. 09进程与定时任务
  10. 10网络命令
  11. 11系统管理与编辑器
  12. 12综合练习

管道与文本处理

grep、sed、awk、重定向。

35 分钟

管道与文本处理

这一课你将能做到

  1. 用管道把 grep、sort、uniq、wc、head 串起来,一条命令统计出 Nginx 日志里访问量 Top 10 的 IP
  2. 熟练使用 grep 的 -i / -v / -n / -r / -A / -B / -C / -E 选项,在 Spring Boot 日志里精准定位异常上下文
  3. 会用 tail -f 实时盯日志、用 awk/sed 完成字段提取和批量替换

为什么要学

线上排查问题的 80% 时间在翻日志。Spring Boot 一个应用一天能产出几百 MB 日志,IDE 都打不开。但 Linux 的设计哲学是「每个程序只做一件事,组合起来解决复杂问题」——grep 负责过滤、awk 负责取列、sort/uniq 负责统计、tail -f 负责实时盯,管道 | 把它们串成流水线。掌握这条流水线,你排查问题的速度比同事快一个量级,面试聊「怎么排查线上 502」时也能直接甩出命令。

人话:管道就像工厂流水线。原料(日志文件)先进 grep 这台机器筛掉杂质,半成品流进 awk 切成规格件,再进 sort/uniq 打包计数,最后 head 装箱出货。每台机器只干一件事,但串起来威力巨大。

先建立图像

一条经典流水线的数据流转:

nginx access.log(100 万行)
      │ cat / 直接读
      ▼
grep " 502 "      → 只剩错误行(比如 3000 行)
      ▼
awk '{print $1}'  → 每行只留第 1 列:客户端 IP
      ▼
sort              → 相同 IP 排到一起
      ▼
uniq -c           → 每种 IP 计数:312 1.2.3.4 ...
      ▼
sort -rn          → 按次数从大到小排
      ▼
head -5           → 取前 5 名(Top 5 攻击源)

日志行的列号直觉(Nginx 默认 combined 格式):

192.168.1.10 - - [01/Oct/2026:10:00:00] "GET /api/user HTTP/1.1" 200 1024
  $1        $2 $3 $4                $5...                $9  $10

awk 里 $1 是第一列,$NF 是最后一列,$0 是整行。

跟着做

步骤 1:造一份仿真日志

mkdir -p ~/pipe-lab && cd ~/pipe-lab
cat > access.log << 'EOF'
192.168.1.10 - - [01/Oct/2026:10:00:01 +0800] "GET /api/user HTTP/1.1" 200 1024
192.168.1.20 - - [01/Oct/2026:10:00:02 +0800] "GET /api/user HTTP/1.1" 502 512
192.168.1.10 - - [01/Oct/2026:10:00:03 +0800] "POST /api/order HTTP/1.1" 200 2048
192.168.1.30 - - [01/Oct/2026:10:00:04 +0800] "GET /api/user HTTP/1.1" 502 512
192.168.1.20 - - [01/Oct/2026:10:00:05 +0800] "GET /static/logo.png HTTP/1.1" 200 9999
192.168.1.10 - - [01/Oct/2026:10:00:06 +0800] "GET /api/user HTTP/1.1" 502 512
192.168.1.40 - - [01/Oct/2026:10:00:07 +0800] "GET /actuator/health HTTP/1.1" 200 15
EOF

<< 'EOF' 是 here-document,从终端直接粘多行进文件,引号包住 EOF 表示不做变量展开。

步骤 2:grep 基础五连

grep "502" access.log                 # 含 502 的行
grep -c "502" access.log              # 只数行数:3
grep -n "502" access.log              # 带行号,方便回头定位
grep -v "200 " access.log             # 反选:不含 200 的行
grep -i "error" app.log               # 忽略大小写
grep -E "502|504" access.log          # 扩展正则:502 或 504

步骤 3:上下文抓取(排查异常堆栈神器)

# 造一份 Java 报错日志
printf '2026-10-01 INFO started\n2026-10-01 ERROR NullPointer\n\tat com.x.Service.run(Service.java:20)\n\tat com.x.Main.main(Main.java:8)\n2026-10-01 INFO recovered\n' > app.log

grep -A 2 "ERROR" app.log    # 匹配行 + 之后 2 行(After,堆栈主体)
grep -B 1 "ERROR" app.log    # 匹配行 + 之前 1 行(Before,触发前的请求)
grep -C 1 "ERROR" app.log    # 前后各 1 行(Context)

线上翻异常:grep -A 20 "NullPointerException" app.log | head -50,把堆栈一次拉出来。

步骤 4:流水线统计 Top IP

grep " 502 " access.log | awk '{print $1}' | sort | uniq -c | sort -rn
# 期望输出:
#   2 192.168.1.10
#   1 192.168.1.20
#   1 192.168.1.30

拆开看:grep 过滤 → awk 取第 1 列 → sort 让相同 IP 相邻(uniq 只认相邻重复)→ uniq -c 计数 → sort -rn 按数字倒序。想看访问总量 Top:去掉 grep 直接 awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -3。

步骤 5:awk 取列与条件过滤

awk '{print $1, $9}' access.log         # IP 和状态码
awk '$9 == 502 {print $1}' access.log   # 状态码等于 502 的 IP
awk -F: '{print $1}' /etc/passwd        # -F 指定冒号为分隔符,取用户名
awk 'END{print NR}' access.log          # 总行数(相当于 wc -l)

步骤 6:sed 批量替换与实时盯梢

sed 's/502/BAD_GATEWAY/' access.log           # 每行第一个 502 替换,输出到屏幕
sed -E 's/GET /api\/user/GET \/api\/v2\/user/' access.log   # 扩展正则替换
sed -i.bak 's/8080/8081/g' nginx.conf         # 直接改文件,g 全局替换,.bak 留备份

tail -f /opt/myapp/logs/app.log               # 实时追加显示,Ctrl+C 退出
tail -f app.log | grep --line-buffered "ERROR"  # 盯着日志只看错误行
tail -n 200 app.log                            # 最近 200 行

讲清楚要点

  1. 管道只传 stdout,不传 stderr:grep xxx app.log 2>/dev/null | wc -l 里 2>/dev/null 是丢弃错误流。想连错误一起统计用 cmd 2>&1 | ...。
  2. uniq 必须先 sort:uniq 只合并「相邻」重复行,乱序数据直接 uniq 得到的统计是错的。sort | uniq -c 是固定搭配。
  3. grep -r 可递归搜目录:grep -rn "spring.datasource" /opt/myapp/config/,在配置目录里找某个配置项是日常高频操作,-n 告诉你文件和行号。
  4. awk 默认按空白切列,-F 换分隔符:日志、/etc/passwd(冒号分隔)、CSV(逗号分隔)都能处理。$NF 最后一列在 URL 带空格时会失效,此时优先用正则提取。
  5. sed -i 直接改文件,务必先备份或先预览:先跑不带 -i 的命令看输出,确认无误再加 -i.bak。生产配置文件被 sed 改错一行,Nginx 起不来就是故障。
  6. tail -f 盯日志时配合 grep 过滤:全量刷屏会把关键行冲走;grep --line-buffered 避免缓冲导致输出延迟。想看错误但保留上下文,用 grep -C 3。

你可能会懵的地方

  • awk 和 sed 到底谁干嘛? 一句话:awk 按「列」处理,sed 按「行」处理。取第 3 列、按条件汇总统计用 awk;删除行、替换字符串用 sed。两者都支持正则,互相能干对方的活,但各有所长。

  • grep " 502 " 前后为什么要加空格? 防止误匹配:15024、5021 这些数字里也含「502」。加上空格后匹配的是独立的状态码字段。更严谨的写法是 awk '$9==502',按列精确匹配。

  • 单引号和双引号在这个场景怎么选? grep/awk/sed 的模式里没有变量就用单引号(Shell 不做任何展开,模式原样传递);需要把 Shell 变量拼进模式用双引号:grep "$DATE" app.log。

  • 日志文件几个 GB,grep 会很慢吗? grep 是流式逐行读,内存占用极小,1GB 日志秒级出结果。真正要避免的是 cat 大文件 直接刷屏——永远在后面接 | head 或 | grep,不要裸看。

  • 2>&1 是什么意思? 把标准错误(2)重定向到标准输出(1)当前指向的地方。nohup java -jar app.jar > app.out 2>&1 & 就是「输出和报错都写进 app.out」——第 9 课进程部分会正式用到。

错法对照

错法问题正解
uniq -c 前没加 sort统计结果碎片化、错误`sort
sort -rn 写成 sort -r按字符串排序,10 排在 2 前面数值排序必须 -n
sed -i 不留备份直接改生产配置改错无法回滚sed -i.bak 或先手动 cp
双引号里写 awk 模式 $9被 Shell 当变量展开成空awk 模式用单引号
`tail -fgrep ERROR` 输出延迟grep 全缓冲

卡点急救

  1. 管道结果为空:从左往右逐段验证——先跑第一段看有没有输出,再一段段往后接,哪一段开始为空问题就在哪。新手常犯:模式拼写和日志实际大小写/空格不一致。
  2. grep 匹配到大量无关行:模式太宽。用 -w 整词匹配、加锚定(^2026-10-01.*ERROR 只匹配今天开头的错误行)、或用 -E 精确枚举多个状态码。
  3. tail -f 报 file truncated 或文件轮转后失效:日志按天切割后 inode 变了,用 tail -F(大写)代替,它会跟踪文件名重新打开。

面试常问

  • 统计 Nginx 日志中访问最多的前 10 个 IP,怎么写? awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10。追问「为什么 uniq 前要 sort」:uniq 只合并相邻重复;追问「为什么 -rn」:按第一列数字倒序。能完整解释每个环节才是满分答案。

  • 怎么在日志里快速定位一次异常的完整堆栈? 先 grep -n "Exception" app.log | tail 拿到行号,再用 sed -n '开始行,结束行p' app.log 精确截取;或一步 grep -A 20 "NullPointerException" app.log | head -50。实时排查则 tail -f app.log | grep -A 20 --line-buffered "Exception"。

  • awk 取列和 cut 取列有什么区别,怎么选? cut 轻量,cut -d: -f1 按单字符分隔符取列,适合格式极规整的数据;awk 自带空白容错(连续空格视为一个分隔符),支持条件过滤、计算、汇总,功能全面。日志处理默认 awk;格式绝对规整且追求速度用 cut。

自测三问

  1. grep -C 3 "ERROR" app.log 中 -C 是什么含义?
  2. 写出统计 access.log 中 504 状态码出现次数的完整管道。
  3. uniq 为什么必须搭配 sort 使用?

小结一句话

grep 过滤、awk 取列、sort+uniq 统计、head 收尾、tail -f 实时盯——这条流水线是日志排查的母语,练熟它就掌握了线上运维一半的日常。

延伸

  • GNU Grep 官方手册
  • 下一课:变量与字符串——让脚本接收参数、记住状态、拼出灵活的命令。
加载本节练习…
← 上一课文件操作与权限下一课 →变量与字符串