← 返回 Linux命令图解 目录
📝 Linux文本处理图解
管道/grep/sed/awk/重定向/find+xargs · SVG可视化 + 生活类比 + 真实输出
① 管道与重定向
管道(pipe) = 工厂流水线:上一个工位的产出,直接送到下一个工位的入口。
• `cat log.txt | grep ERROR | wc -l` = 读文件→筛错误→数行数
• 前一个命令的stdout变成后一个命令的stdin
重定向 = 换出口:本来输出到屏幕的数据,改到文件里。
• > = 换新出口(覆盖写入)
• >> = 在原出口旁边加一个(追加写入)
• 2> = 错误信息换出口
• < = 从文件读输入(而不是键盘)
管道(pipe)流水线 + 重定向换出口
$ # 管道实战:统计access.log中各IP的访问次数TOP10
$ cat access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -10
1256 192.168.1.100
834 10.0.0.55
412 172.16.0.10
# ↑次数 ↑IP地址
$ # 重定向实战:备份+错误分离
$ java -jar app.jar > app.log 2>&1
# 所有输出(标准+错误)都写入app.log
$ # tee:同时输出到屏幕和文件
$ ./build.sh 2>&1 | tee build.log
# 屏幕上看到实时输出,同时也保存到build.log
② grep搜索
grep = 搜查队:在一堆文件里找到你要的那行。就像警察在监控录像里找特定的人。
• grep "ERROR" log.txt = 找所有含ERROR的行
• grep -r "TODO" src/ = 递归搜src下所有文件
• grep -v "DEBUG" log.txt = 排除DEBUG行(反向搜索)
• grep -c "ERROR" log.txt = 数有多少行ERROR
• grep -i "error" log.txt = 大小写不敏感
# 1. 基本搜索
$ grep "ERROR" app.log
2024-06-14 10:23:45 ERROR [main] Database connection failed
2024-06-14 10:25:12 ERROR [main] Timeout waiting for response
# 2. 递归搜索 + 显示行号
$ grep -rn "TODO" src/
src/main/java/App.java:42: // TODO: handle null case
src/main/java/Service.java:88: // TODO: add retry logic
# 3. 排除某些行(-v) + 只显示匹配的文件名(-l)
$ grep -rvl "DEBUG" *.log
app.log # 含非DEBUG行的日志文件
# 4. 正则搜索:找IP地址
$ grep -E "([0-9]{1,3}\.){3}[0-9]{1,3}" access.log
192.168.1.100 - - [14/Jun/2024:10:23:45] "GET /api/users"
# 5. 统计匹配行数(-c)
$ grep -c "ERROR" app.log
23
# 6. 显示匹配前后各2行(-C)
$ grep -C 2 "OutOfMemoryError" app.log
2024-06-14 10:23:43 INFO [main] Starting batch job
2024-06-14 10:23:44 WARN [main] Memory usage at 95%
2024-06-14 10:23:45 ERROR [main] java.lang.OutOfMemoryError: Java heap space
2024-06-14 10:23:46 INFO [main] Attempting restart...
2024-06-14 10:23:47 INFO [main] Restart successful
③ sed替换
sed = 批量修改工:在流水线上逐行处理文本,找到符合规则的行就改。
• sed 's/old/new/' = 把每行第一个old换成new
• sed 's/old/new/g' = 把所有old换成new(g=global)
• sed -i = 直接改原文件(-i = in-place,像直接在原件上改)
• sed -n '5,10p' = 只看第5-10行
$ # 替换文本(默认只输出不改文件)
$ echo "Hello World" | sed 's/World/Linux/'
Hello Linux
$ # 全局替换(g)
$ echo "aaa bbb aaa" | sed 's/aaa/xxx/g'
xxx bbb xxx
$ # 直接修改文件(-i) + 备份(-i.bak)
$ sed -i.bak 's/http:/https:/g' config.properties
# 原文件改为https,备份文件为config.properties.bak
$ # 删除空行
$ sed '/^$/d' file.txt
# /^$/ = 匹配空行, d = delete
$ # 删除注释行(#开头的)
$ sed '/^#/d' config.conf
$ # 只显示第5-10行
$ sed -n '5,10p' bigfile.log
$ # 在第3行后插入内容
$ sed '3a\new line here' file.txt
$ # 生产实战:替换配置文件中的数据库地址
$ sed -i 's/jdbc:mysql:\/\/localhost/jdbc:mysql:\/\/prod-db-server/g' application.properties
④ awk分析
awk = 数据分析师:按列分析文本,能做计算、格式化、统计。比sed强在"理解列"。
• $0 = 整行,$1 = 第1列,$2 = 第2列...
• -F = 指定分隔符(默认空格)
• BEGIN{} = 开始前做的事,END{} = 结束后做的事
• NR = 行号,NF = 当前行有多少列
awk逐行处理模型:BEGIN初始化 → 逐行处理 → END汇总
$ # 1. 提取第1列(IP)
$ awk '{print $1}' access.log
192.168.1.100
10.0.0.55
$ # 2. 指定分隔符(-F):分析CSV
$ awk -F',' '{print $1, $3}' data.csv
张三 28
李四 35
$ # 3. 统计各IP访问次数(TOP10)
$ awk '{count[$1]++} END {for(ip in count) print count[ip], ip}' access.log | sort -rn | head -10
1256 192.168.1.100
834 10.0.0.55
$ # 4. 计算第3列的平均值
$ awk '{sum+=$3; n++} END {print "avg:", sum/n}' data.txt
avg: 42.5
$ # 5. 条件过滤:只看状态码>=400的
$ awk '$7 >= 400 {print $1, $7, $5}' access.log
192.168.1.100 500 "POST /api/pay
10.0.0.55 404 "GET /old-page
$ # 6. 格式化输出(printf)
$ awk '{printf "%-20s %10d\n", $1, $8}' access.log
192.168.1.100 1234
10.0.0.55 567
⑤ 组合实战技
组合技 = 多个工人协同干活:grep找人 → awk取数据 → sort排好 → uniq去重统计 → head取前N个
5大生产组合技:日志分析/磁盘清理/批量替换/日志归档/性能分析
terminal — find + xargs实战
# find基本用法
$ find /var/log -name "*.log" -mtime +7
# 在/var/log下找7天前修改的.log文件
$ find . -type f -name "*.java" | head -5
./src/main/java/App.java
./src/main/java/Service.java
./src/main/java/Controller.java
# xargs:把find的输出变成下一个命令的参数
$ find . -name "*.java" | xargs grep "TODO"
# 等价于: grep "TODO" ./src/.../App.java ./src/.../Service.java ...
# ⚠️ 文件名有空格时用 -print0 + xargs -0
$ find . -name "*.java" -print0 | xargs -0 grep "TODO"
# 批量删除7天前的日志
$ find /var/log -name "*.log" -mtime +7 -delete
# 比用xargs rm更安全
# 统计Java代码行数
$ find . -name "*.java" | xargs wc -l | tail -1
125678 total
⑥ 面试速答
管道(pipe)和重定向有什么区别?
管道:连接两个命令,前一个的stdout变成后一个的stdin。命令之间的数据流。
重定向:改变数据的来源或去向,从/到文件。命令和文件之间的数据流。
关键区别:管道连接的是命令和命令,重定向连接的是命令和文件。
grep、sed、awk分别适合什么场景?
grep:查找/过滤。只关心"有没有""在哪",不做修改。最强在正则匹配。
sed:替换/删除/插入。逐行修改文本。最强在流式编辑。
awk:分析/统计/计算。按列处理,能做数学运算。最强在数据统计。
记忆口诀:grep找、sed改、awk算。
sed -i和sed有什么区别?为什么sed -i有风险?
sed:只输出处理结果到屏幕,不改原文件。
sed -i:直接修改原文件(in-place)。
风险:
1. 没有备份!改错了无法回退
2. 建议:先不加-i看结果,确认无误再加-i
3. 安全做法:`sed -i.bak 's/old/new/g' file` → 自动创建file.bak备份
awk中NR和NF是什么意思?
NR = Number of Records(行号)。当前处理的是第几行。
NF = Number of Fields(列数)。当前行被分成了几列。
常见用法:
- `awk 'NR==1'` → 只取第一行(表头)
- `awk 'NR>1'` → 跳过第一行
- `awk '{print $NF}'` → 取最后一列
- `awk 'END{print NR}'` → 总行数
find和xargs为什么要配合-print0和-0?
默认情况下,find用换行符分隔结果,xargs用空格分隔参数。如果文件名里有空格或特殊字符,就会被拆错。
`-print0`:find用null字符(\0)分隔结果
`xargs -0`:xargs按null字符分隔参数
这样"my file.java"会被当成一个文件名,而不是"my"和"file.java"两个。
如何快速统计access.log中各HTTP状态码的分布?
一行搞定:`awk '{print $9}' access.log | sort | uniq -c | sort -rn`
解读:
1. awk取状态码列($9是access.log中的HTTP状态码)
2. sort排序(相同状态码排在一起)
3. uniq -c去重并计数
4. sort -rn按计数降序
输出示例:
1256 200
234 404
56 500
12 301
>file 2>&1 和 2>&1 >file有什么区别?
>file 2>&1:✅ stdout先重定向到file,stderr再重定向到stdout(此时已经是file)。结果:stdout和stderr都到file。
2>&1 >file:❌ stderr先重定向到stdout(此时还是屏幕),然后stdout重定向到file。结果:stdout到file,stderr到屏幕!
关键:重定向是从左到右依次执行的,顺序很重要!
记忆:先定好出口(>file),再把其他流(2>&1)指向这个出口。
核心三原则速记
文本处理三原则:① grep 找行、sed 改行、awk 按列处理,各有所长 → ② 管道 | 串联命令,前一个的输出是后一个的输入 → ③ 正则表达式是核心技能,基础正则够用 90% 场景