Linux 文本处理 — 三剑客
如果说前面几篇是"会用 Linux",这一篇就是"爱上 Linux"。grep、sed、awk 被称为文本三剑客,加上 find,组合起来能完成 Python 几十行代码才能做的事。日志分析、批量改名、数据提取都离不开它们。
1. grep:按行搜索
grep 是 "global regex print" 的缩写(源自古老 Unix 的 g/re/p 命令)。作用就一个:在文件或输入里找出含某个模式的行。它是排查日志最常用的工具。
# grep:global regex print,按行搜索(全局正则匹配打印)
# 基本用法:grep "关键字" 文件
grep "error" app.log # 找含 error 的行
grep "ERROR" app.log # 大小写敏感(ERROR 和 error 不同)
# 常用选项
grep -i "error" app.log # -i:ignore case,不分大小写
grep -n "TODO" *.py # -n:显示行号
grep -r "TODO" ./src # -r:recursive,递归搜子目录
grep -v "debug" app.log # -v:invert,反向(不含 debug 的行)
grep -c "error" app.log # -c:count,只输出匹配行数
grep -l "error" *.log # -l:只输出含匹配的文件名
grep -w "error" app.log # -w:word,整词匹配(不匹配 errors)
grep -A 2 "error" app.log # -A 2:匹配行 + 后 2 行(After)
grep -B 2 "error" app.log # -B 2:匹配行 + 前 2 行(Before)
grep -C 2 "error" app.log # -C 2:前后各 2 行(Context)
# 配合管道
ps aux | grep nginx # 在进程列表里找 nginx
dmesg | grep -i usb # 在内核日志里找 USB 信息几个高频组合:grep -rn 在代码库里找关键字、grep -v 排除无关行、grep -i 忽略大小写。ps aux | grep nginx(找进程)、history | grep ssh(找历史命令)是日常必备。
2. sed:流编辑器
sed 是 "stream editor" 的缩写,最常用于批量替换。它的语法乍看奇怪,但用熟了几秒钟就能搞定一批文件的改名、改字段。
# sed:stream editor,流编辑器(常用于批量替换)
# 替换(substitute):s/旧/新/
sed 's/foo/bar/' file.txt # 每行第一个 foo 换成 bar
sed 's/foo/bar/g' file.txt # g:global,所有 foo 都换
sed 's/foo/bar/gi' file.txt # i:不分大小写
sed 's/foo/bar/2' file.txt # 只换每行第 2 个
# 直接修改原文件(谨慎!)
sed -i 's/foo/bar/g' file.txt # -i:in-place,直接改原文件
sed -i.bak 's/foo/bar/g' file.txt # -i.bak:改之前备份成 file.txt.bak
# 定址:指定哪些行要处理
sed '3d' file.txt # 删除第 3 行
sed '2,5d' file.txt # 删除第 2 到 5 行
sed '/^#/d' file.txt # 删除以 # 开头的行(去掉注释)
sed '5s/foo/bar/' file.txt # 只在第 5 行替换
# 多条命令用 -e
sed -e 's/foo/bar/g' -e 's/baz/qux/g' file.txt
# 插入和追加
sed '2i 新行内容' file.txt # 第 2 行前插入
sed '2a 新行内容' file.txt # 第 2 行后追加最常用的就是 sed -i 's/旧/新/g':-i 直接改原文件,g 全局替换。用 -i 前一定先备份或用 -i.bak 让 sed 自动备份,因为它没有"撤销"。
3. awk:按列处理
awk(三位作者 Aho、Weinberger、Kernighan 姓氏首字母)是一个完整的文本处理语言,最擅长按列处理表格数据。ps、df、ls -l 输出的列式数据,awk 一行就能提取你想要的列。
# awk:按列处理(适合表格、CSV、ps 输出)
# 基本格式:awk '条件 {动作}' 文件
# 默认按空格/Tab 分列,$0=整行 $1=第一列 $2=第二列...
# 打印特定列
ps aux | awk '{print $1, $11}' # 打印第 1 列(用户)和第 11 列(命令)
df -h | awk '{print $1, $5}' # 打印文件系统和占用率
awk -F',' '{print $2}' data.csv # -F',' 指定逗号为分隔符(CSV)
# 条件过滤(NR=行号,NF=列数)
awk 'NR==1' file.txt # 只打印第 1 行
awk 'NR>1' file.txt # 跳过表头(从第 2 行开始)
awk 'NR>1 {print $5}' file.txt # 跳过表头打印第 5 列
awk 'NF>3' file.txt # 只打印列数大于 3 的行
awk '$3 > 100' scores.txt # 第 3 列大于 100 的行
awk '$1 == "ERROR" {print $0}' log # 第 1 列等于 ERROR 的整行
# 算术:统计某列的总和
awk '{sum += $2} END {print sum}' sales.txt
# 统计第 2 列总和,END 在所有行处理完后执行
# 计算平均值
awk '{sum+=$2; n++} END {print sum/n}' scores.txt关键概念:$0 整行、$1 第一列、$2 第二列;NR 当前行号、NF 当前行列数;-F 指定分隔符(默认空格)。awk '{sum+=$2} END {print sum}' 统计某列总和,在分析销售、监控数据时极其方便。
4. find:查找文件
find 是功能最强的文件查找工具。和按名字模糊匹配的 locate 不同,find 可以按名字、类型、大小、时间、权限等几乎所有属性查找,还能对结果执行命令。
# find:在目录树里查找文件(功能最强)
# 按文件名
find . -name "*.py" # 当前目录递归找所有 .py 文件
find . -name "*.py" -type f # -type f 只找普通文件
find /var/log -name "*.log"
find . -iname "*.JPG" # -iname:不分大小写
# 按类型
find . -type d -name "node_modules" # 只找目录(d)
find . -type l # 只找软链接(l)
# 按时间(单位:天)
find . -mtime -7 # 7 天内修改过的(- 表示内)
find . -mtime +30 # 30 天前修改过的(+ 表示前)
find . -mmin -60 # 60 分钟内修改过的
# 按大小
find . -size +100M # 大于 100MB 的文件
find . -size -1k # 小于 1KB 的文件
# 找到后执行动作(-exec)
find . -name "*.log" -exec rm {} \; # 删掉找到的日志
find . -name "*.bak" -exec mv {} /tmp/ \; # 移到 /tmp
find . -name "*.js" -exec wc -l {} \; # 统计每个文件行数
find . -name "*.py" -exec grep "TODO" {} \; # 在找到的文件里搜 TODO
# 配合 xargs 更高效(下一篇管道会讲)
find . -name "*.log" | xargs grep "error"find ... -exec 是 find 的杀手锏,但语法有个怪癖:命令必须以 \; 结尾(转义分号),{} 代表找到的每个文件。批量清理日志、批量改权限都用得上。
5. 其他高频文本工具
三剑客之外,还有几个小工具也极常用,组合起来威力倍增:
# 其他高频文本工具
# wc:word count,统计
wc -l file.txt # 行数
wc -w file.txt # 单词数
wc -c file.txt # 字节数
# sort:排序
sort file.txt # 默认按字典序
sort -n nums.txt # 按数字排序
sort -rn nums.txt # 反向数字排序(从大到小)
sort -u names.txt # 排序并去重
sort -t',' -k2 sales.csv # 按第 2 列排序(-t 分隔符)
# uniq:去重(只去相邻的重复行,所以先 sort)
sort names.txt | uniq # 排序后去重
sort names.txt | uniq -c # 去重并统计每条出现次数
sort names.txt | uniq -c | sort -rn # 出现次数从多到少(超常用)
# cut:按列切
cut -d',' -f1,3 data.csv # -d 分隔符,-f 取第 1、3 列
cut -c1-10 file.txt # 取每行的第 1-10 个字符
# tr:translate,字符转换
echo "Hello" | tr 'a-z' 'A-Z' # 小写转大写
echo "a,b,c" | tr ',' '\n' # 逗号换行
cat file.txt | tr -d '\r' # 删除所有 \r(Windows 换行转 Unix)6. 经典组合:统计访问日志里出现最多的 IP
这是面试和实战都会问的经典场景,完美展示了 Unix "小工具组合"的哲学:
cat access.log— 读出文件全部行awk '{print $1}'— 取第 1 列(IP 地址)sort— 排序,让相同 IP 相邻uniq -c— 去重并计数sort -rn— 按数量从大到小排head -n 10— 取前 10
整套链路就一行管道命令,几十秒出结果。这就是命令行的魅力。
7. 正则表达式速成
grep、sed、awk 都依赖正则表达式,掌握几个基础元字符就够日常用:
.任意单个字符;*前一个字符出现 0 次或多次。^行首;$行尾。^#匹配以 # 开头的行。[abc]方括号里任一字符;[^abc]不在方括号里的。\\d数字(grep 需-E或-P);\\w字母数字下划线。+一个或多个;?零个或一个(扩展正则,grep 加-E)。
用 grep -E(或别名 egrep)启用扩展正则,元字符更省事。
小结
这一篇你认识了 Linux 文本处理的四大金刚:grep 找行、sed 改字符、awk 处理列、find 找文件,以及 sort、uniq、wc、cut、tr 等辅助。下一篇讲进程管理——看看程序是怎么跑起来的、怎么杀掉卡住的进程。
← 上一篇 Linux 权限管理
下一篇 Linux 进程管理 →