← 返回 Linux命令图解 目录

📝 Linux文本处理图解

管道/grep/sed/awk/重定向/find+xargs · SVG可视化 + 生活类比 + 真实输出

① 管道与重定向

管道(pipe) = 工厂流水线:上一个工位的产出,直接送到下一个工位的入口。
• `cat log.txt | grep ERROR | wc -l` = 读文件→筛错误→数行数
• 前一个命令的stdout变成后一个命令的stdin

重定向 = 换出口:本来输出到屏幕的数据,改到文件里。
> = 换新出口(覆盖写入)
>> = 在原出口旁边加一个(追加写入)
2> = 错误信息换出口
< = 从文件读输入(而不是键盘)
管道与重定向 — 流水线 + 换出口 管道(pipe) = 流水线 cat log.txt 读文件 | grep ERROR 筛出错误行 | awk '{print $3}' 提取第3列 | sort | uniq -c 排序+去重计数 重定向 = 换出口 标准输出重定向(stdout → 文件) cmd > file # 覆盖写入 cmd >> file # 追加写入 cmd 2> err.log # 错误→文件 cmd > log 2>&1 # 输出+错误都到log 新版写法: cmd >& log 或 cmd 2>&1 | tee log 输入重定向(文件 → stdin) cmd < file # 从文件读输入 cmd <<EOF # Here Document cmd <<<"text" # Here String 常用:mysql -u root < backup.sql 常用:while read line; do ... done < file
管道(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 = 大小写不敏感
terminal — grep实战
📂 text-processing/
grep-demo.sh
sed-demo.sh
awk-demo.sh
# 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逐行处理模型 192.168.1.100 - - [14/Jun/2024] "GET /api/users" 200 1234 awk按空格分列 → $1 $2 $3 $4 $5 $6 $7 $8 $1=IP $2=- $3=- $4=[date] $5="GET $7=200 $8=1234 BEGIN { 初始化 } 开始前:设变量、打表头 { 逐行处理 } 每行执行:过滤/计算/累加 END { 汇总输出 } 结束时:算平均/打汇总
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大生产组合技 🥇 统计日志ERROR频率TOP10 grep "ERROR" app.log | awk '{print $5}' | sort | uniq -c | sort -rn | head -10 🥈 找出占用磁盘最大的目录 du -h --max-depth=1 / | sort -rh | head -10 🥉 批量替换所有Java文件中的包名 find . -name "*.java" | xargs sed -i 's/com.old/com.new/g' ④ 查找7天前的日志并压缩 find /var/log -name "*.log" -mtime +7 | xargs gzip ⑤ 分析access.log各URL的响应时间P99 awk '{print $7, $NF}' access.log | sort -k2 -n | awk '{a[NR]=$2} END {print "P99:", a[int(NR*0.99)]}'
5大生产组合技:日志分析/磁盘清理/批量替换/日志归档/性能分析
terminal — find + xargs实战
📂 text-processing/
grep-demo.sh
sed-demo.sh
awk-demo.sh
find-xargs.sh
# 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% 场景