LinuxNotes

第 06 章:文本处理

zjc 于 2026-01-06 发布

这是《Linux 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 日志、配置、CSV、JSON、命令输出都是文本。Linux 提供了一组小而强的工具:grep 查、sed 改、awk 切、sort 排、uniq 统计、cut 取列。组合它们,可以在几分钟内完成大量定位工作。

6.1 查看与统计

cat app.log
less app.log
head -n 100 app.log
tail -n 100 app.log
tail -F app.log
wc -l app.log
wc -c app.log

less 常用操作:

按键 作用
/word 向下搜索
?word 向上搜索
n 下一个
N 上一个
G 到文件末尾
g 到文件开头
q 退出

编码问题:

file app.log
iconv -f GBK -t UTF-8 old.log > new.log

6.2 grep 详解

基础:

grep ERROR app.log
grep -i error app.log
grep -n ERROR app.log
grep -v DEBUG app.log
grep -E 'ERROR|WARN' app.log
grep -F '10.20.30.40' access.log
grep -c ERROR app.log
grep -r "listen 80" /etc/nginx

上下文:

grep -A 5 ERROR app.log
grep -B 5 ERROR app.log
grep -C 10 ERROR app.log

文件过滤:

grep -r --include='*.log' ERROR /var/log
grep -r --exclude='*.gz' ERROR /var/log

正则示例:

grep -E '^[0-9]{4}-[0-9]{2}-[0-9]{2}' app.log
grep -E 'status=[45][0-9]{2}' access.log
grep -E '\btimeout\b' app.log

grep 按行匹配,不适合解析复杂嵌套 JSON。结构化数据优先使用 jq

6.3 cut 与 awk

cut 适合简单分隔符:

cut -d: -f1 /etc/passwd
cut -d, -f1,3 users.csv
cut -c1-20 app.log

awk 按字段处理:

awk '{print $1}' access.log
awk -F: '{print $1, $7}' /etc/passwd
awk -F, '{print $1, $3}' users.csv
awk 'END {print NR}' app.log

条件:

awk '$9 >= 500 {print $1, $7, $9}' access.log
awk '/ERROR/ {print $1, $5}' app.log
awk -F, '$3 > 100 {sum += $3} END {print sum}' orders.csv

格式化:

awk '{printf "%-20s %10s\n", $1, $7}' access.log

内置变量:

变量 含义
NR 当前行号
NF 当前字段数
FS 输入分隔符
OFS 输出分隔符
FILENAME 当前文件名

示例:

awk -F'|' 'BEGIN{OFS=","} {print $1, $3, $5}' events.log

6.4 sed 详解

替换:

sed 's/ERROR/error/' app.log
sed 's/ERROR/error/g' app.log
sed 's/port=8080/port=9090/' app.conf
sed -i 's/port=8080/port=9090/' app.conf

删除:

sed '/^#/d' app.conf
sed '/^$/d' app.conf
sed '1d' users.csv
sed '$d' users.csv

显示:

sed -n '10,20p' app.log
sed -n '/ERROR/p' app.log

插入:

sed '/^\[server\]/a listen=9090' app.ini
sed '1i # generated by ops' app.conf

安全修改:

cp app.conf app.conf.bak
sed -i 's/debug=true/debug=false/' app.conf
diff -u app.conf.bak app.conf

GNU sed 与其他平台的参数细节可能不同。跨平台脚本应先测试。

6.5 sort 与 uniq

排序:

sort app.log
sort -r app.log
sort -n sizes.txt
sort -h sizes.txt
sort -t, -k2 users.csv
sort -u ips.txt

统计:

sort ips.txt | uniq -c
sort ips.txt | uniq -c | sort -nr
sort ips.txt | uniq -d
sort ips.txt | uniq -u

经典统计:访问次数前 10 的 IP:

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

uniq 只合并相邻重复行,所以通常要先 sort

6.6 join 与 diff

join 合并两个已排序文件:

join -t, -1 1 -2 1 users.csv orders.csv

diff 比较差异:

diff -u old.conf new.conf
diff -rq /opt/app/releases/v1 /opt/app/releases/v2

配置发布前建议保存差异:

diff -u /etc/nginx/nginx.conf.bak /etc/nginx/nginx.conf

6.7 正则表达式

常用元字符:

符号 含义
. 任意单个字符
* 前一个元素零次或多次
+ 一次或多次,扩展正则
? 零次或一次,扩展正则
^ 行首
$ 行尾
[abc] 字符集合
[^abc] 排除字符
\b 单词边界
() 分组
|

示例:

grep -E '^2026-08-25.*(ERROR|WARN)' app.log
grep -E 'user_id=[0-9]+' app.log
sed -E 's/([0-9]{3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/<ip>/g' access.log

日志脱敏要在采集或展示层统一实现,不能只依赖临时命令。

6.8 jq 处理 JSON

安装:

sudo apt install jq
sudo dnf install jq

格式化:

echo '{"id":1,"status":"PAID"}' | jq .
jq . events.json

取字段:

jq '.status' event.json
jq '.user.id' event.json
jq '.items[0].sku' event.json

数组:

curl -s http://localhost:8080/api/users | jq '.[] | {id, name}'
jq '[.[] | select(.status == "PAID")]' orders.json
jq 'length' orders.json

统计:

jq -r '.[] | .city' orders.json | sort | uniq -c
jq -r '.[] | [.id, .amount] | @tsv' orders.json

6.9 xargs 与并行

xargs 把输入变成命令参数:

cat hosts.txt | xargs -n1 ping -c1
find /var/log -name '*.gz' | xargs ls -lh
find /tmp -name '*.tmp' | xargs rm

处理空格和特殊字符:

find /opt/app -type f -name '*.log' -print0 | xargs -0 grep ERROR

并行:

cat hosts.txt | xargs -P 10 -n1 ping -c1

删除类命令先预览:

find /tmp -name '*.tmp' -print
find /tmp -name '*.tmp' -print0 | xargs -0 rm

6.10 生产日志案例

案例一:统计错误分布

grep -E 'ERROR|WARN' app.log |
  awk -F'|' '{print $2}' |
  sort |
  uniq -c |
  sort -nr |
  head -20

案例二:定位某用户请求

grep 'userId=10001' app.log
grep 'trace_id=7f3a' app.log

案例三:压缩日志查找

zgrep ERROR app.log.gz
zcat app.log.gz | grep ERROR
zless app.log.gz

案例四:统计 HTTP 状态

awk '{print $9}' access.log |
  sort |
  uniq -c |
  sort -k2n

Nginx 日志格式不同时,字段位置可能不同,应先看日志格式定义。

6.11 性能与边界

大文件处理:

  1. 先缩小时间范围;
  2. 再按关键字过滤;
  3. 避免无意义的全量 cat
  4. 压缩日志用 zgrep
  5. 超大文本考虑日志平台或数据库。

示例:

grep '^2026-08-25 10:' app.log | grep ERROR

复杂需求不要无限堆管道。当脚本超过一定复杂度,改用 Python、Go 或日志平台更清晰。

本章小结

文本处理遵循“过滤、切分、排序、统计、输出”的思路。grep 找行,awk 处理字段,sed 做流编辑,sortuniq 完成统计,jq 处理 JSON,xargs 把文本转成参数。临时命令高效,但长期方案应沉淀到脚本、监控和日志平台。

思考题

  1. 为什么 uniq 之前通常要 sort
  2. sed -i 修改文件前应做什么?
  3. 如何安全处理包含空格的文件名?
  4. grepjq 各适合什么数据格式?
  5. 设计一条管道统计 Nginx 日志中 5xx 响应最多的 URL。