命令行文本处理工具链:sed、awk、jq 与管道组合

系统掌握 Unix 文本处理工具链:sed 流编辑、awk 字段处理、jq JSON 解析、grep/ripgrep 搜索、sort/uniq/wc、xargs 与管道组合,附一行式速查与数据处理实战场景。

引言

一个不显眼的技能组合能省掉大量「写脚本」时间:sed 处理行、awk 处理字段、jq 处理 JSON、grep 找目标——再用管道串起来。这些工具的共同哲学是「数据流」:每个工具读一行处理一行,输出给下一个。本文按工具逐个讲透,再给 10 个真实场景的管道写法。

前置:/regex-deep-dive/(sed/grep 的正则)、/unicode-encoding-guide/(编码陷阱)。


目录


1. 数据流哲学:管道与工具链

核心模型:每个命令 = 纯函数(stdin → 处理 → stdout),用 | 串联:

cat access.log | grep '500' | awk '{print $1}' | sort | uniq -c | sort -rn

管道设计原则:

原则说明
单工具单职责一个命令只做一件事
流式不落盘中间不写文件,内存里流过
无副作用不改源文件(sed -i 除外)
尽早过滤grep 放最前省后续工作量

心智:管道 = 数据流水线,每个环节窄而快。先想「数据从哪来、要变成什么」再拼工具。


2. grep / ripgrep:精准搜索

grep 基础:

grep 'error' app.log            # 搜索
grep -i 'warn' app.log          # 忽略大小写
grep -n 'error' app.log         # 显示行号
grep -v 'debug' app.log         # 反选(排除)
grep -c 'error' app.log         # 计数
grep -r 'pattern' src/          # 递归目录
grep -l 'pattern' src/*.py      # 只列文件名
grep -A2 -B1 'error' app.log    # 上下文各 2/1 行

ripgrep(rg,现代替代,快 10 倍):

rg 'TODO|FIXME' src/             # 默认递归 + 忽略 gitignore
rg -t py 'def main'              # 只搜 Python
rg --stats 'error' log           # 附统计
rg -uu 'hidden'                  # 包含隐藏文件

正则注意:默认 grep 用基础正则(BRE)——+ ? 需转义 \+ \?;用 grep -E(ERE)或 rg 免转义。

场景命令
快速找关键词rg 'kw' dir/
统计出现行数grep -c pattern file
只看文件名rg -l pattern
排除二进制rg -t 指定类型

3. sed:流式文本编辑

sed 基础(s 替换 + 地址范围):

sed 's/foo/bar/' file          # 每行第一个 foo → bar
sed 's/foo/bar/g' file         # 全部替换(g)
sed -i 's/foo/bar/' file       # 就地修改(注意 -i 改原文件!)
sed -n '5p' file               # 只打印第 5 行
sed -n '1,10p' file            # 打印 1-10 行
sed -n '10,20d' file           # 删除 10-20 行(打印其余)
sed '/^#/d' config             # 删除注释行
sed 's/^/  /' file             # 每行加两空格缩进

地址匹配:

sed -n '/error/p' file         # 打印含 error 的行(等价 grep)
sed '/start/,/end/d' file      # 删 start~end 范围

多用例:模板替换、去尾空格、批量改路径分隔符、日志清洗。

记忆:sed 三板斧 = s(替换)、p(打印)、d(删除)+ 地址定位。加 -n 只打印被选中的行。


4. awk:字段处理语言

awk 处理「列」——默认按空白/制表符切字段,$1 $2 ... $0(整行):

awk '{print $1}' file              # 第一列
awk '{print $1, $NF}' file         # 第一列和最后一列
awk -F',' '{print $2}' data.csv    # 按逗号切分
awk '$3 > 100' file                # 条件过滤(第3列>100)
awk 'NR >= 2 {print $0}' file      # 跳过标题行

awk 程序结构(BEGIN 初始化,主块逐行,END 汇总):

awk 'BEGIN{sum=0} {sum += $3} END{print "总合:", sum}' sales.txt
awk 'BEGIN{max=0} {if ($2 > max) max = $2} END{print "最大:", max}' data

统计出现次数:

awk '{count[$1]++} END{for (k in count) print k, count[k]}' access.log
需求awk 写法
取列$n / $NF(最后列)
自定义分隔-F',' 或 FS=","
数值条件$2 > 100
累计sum += $n
统计分组数组 count[k]++
内置变量NR(行号) NF(字段数) FILENAME

记忆:awk = 迷你语言:条件 {动作} 逐行执行,END 块做汇总,数组当字典用。


5. jq:JSON 处理

jq 是 JSON 的 awk——查询、转换、管道:

jq '.name' user.json                       # 取字段(含引号)
jq -r '.name' user.json                    # 原始字符串(去引号)
jq '.users[]' list.json                    # 数组遍历
jq '.users[] | select(.age > 30)' list     # 过滤
jq '.[0].name' list.json                   # 索引
jq '.users | length' list.json             # 长度
jq 'keys' obj.json                         # 键列表

管道与转换:

jq '.users | map(.name)' list.json                # 提取所有 name
jq 'group_by(.dept) | map({dept: .[0].dept, n: length})' emp.json
jq '{total: .items | length, sum: (.items | map(.price) | add)}' cart.json
jq -c '.[] | {id, name}' big.json > compact.ndjson  # 转新行分隔 JSON

与管道组合(API 处理):

curl -s 'https://api.github.com/users/xxx/repos' | jq '.[] | .name'
场景写法
取嵌套字段.a.b.c
遍历过滤.arr[] | select(.x > 1)
变换结构{k: .v} / map(...)
求和.arr | map(.v) | add
校验 JSONjq empty file(无输出=合法)
格式化jq . file(美化缩进)

记忆:jq 的 | 与 shell 同哲学——每个 .x/map 是数据变换,select 过滤,{...} 重构。


6. sort / uniq / wc:统计组合

sort file                        # 排序
sort -n file                     # 数值排序(不是字典序!)
sort -rn file                    # 数值倒序
sort -t',' -k2 file              # 按第2列排序
uniq -c file                     # 去重并计数(必须已 sort!)
uniq -d file                     # 只显示重复行
wc -l file                       # 行数
wc -w file                       # 单词数
wc -c file                       # 字节数

经典 Top-N(访问量统计):

awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10
#     取IP列           去重计数      次数倒序      取前10

注意:uniq 只合并相邻重复行——必须 sort 后再用。

组合拳:取字段(awk)→ 去重计数(sort|uniq -c)→ 排序(sort -rn)→ 截取(head)——这是日志分析四件套。


7. cut / paste / tr:列与字符

cut -d',' -f1,3 file.csv        # 按逗号取第1、3列
cut -c1-10 file                 # 取每行前10字符
paste -d',' a.txt b.txt         # 按行合并两个文件(列拼接)
tr 'a-z' 'A-Z' < file           # 大小写转换
tr -d '\r' < win.txt > unix.txt # 删回车(\r)转 Unix 行尾
tr -s ' ' < file                # 压缩连续空格
tr '\n' ',' < list.txt          # 行转逗号分隔

行尾转换(跨平台经典):

sed 's/\r$//' win.txt > unix.txt   # 或
tr -d '\r' < win.txt > unix.txt
需求命令
取列cut -d',' -f1
合并文件列paste -d',' a b
替换字符集tr 'set1' 'set2'
压缩空白tr -s ' '
删除字符tr -d 'x'

8. xargs 与并行执行

xargs:把 stdin 变成命令参数(管道最后一步常用):

find . -name '*.log' | xargs grep 'error'      # 一堆文件里搜
ls *.txt | xargs -n1 wc -l                     # 每个文件单独执行
cat urls.txt | xargs -n1 curl -O               # 逐个下载
find . -name '*.tmp' | xargs rm                # 批量删除

并行(-P):

find . -name '*.jpg' | xargs -P8 -I{} convert {} {}.webp   # 8 并行
cat urls.txt | xargs -P4 -I{} curl -s {} -o {}.html        # 4 并发下载
参数含义
-n1每参数一个命令
-I{}占位符模式({} 代换每个输入)
-P44 个并行进程
-d'\n'按行分隔(含空格文件名安全)

安全提示:文件名含空格/换行时用 find -print0 | xargs -0,防参数错乱。


9. 10 个实战管道场景

1. 日志错误 Top 错误类型:

grep 'ERROR' app.log | awk '{print $5}' | sort | uniq -c | sort -rn

2. 找出访问量最大的 5 个 IP:

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

3. 500 错误来源路径 Top:

grep ' "500 ' access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head

4. 统计每种 HTTP 状态码数量:

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

5. 进程内存 Top 5:

ps aux | awk '$6>0 {print $6, $11}' | sort -rn | head -5

6. 找出 5 分钟内修改的文件:

find . -mmin -5 -type f | head

7. 从 JSON 提取字段写入 CSV:

jq -r '.[] | [.name, .age] | @csv' users.json > out.csv

8. 合并多个日志并去重统计:

cat a.log b.log | sort | uniq -c

9. 删除所有 .tmp 文件:

find . -name '*.tmp' -type f -delete      # 或
find . -name '*.tmp' -type f | xargs rm -f

10. 文本文件行数 + 最大行长度:

wc -l file.txt && awk '{if (length($0)>m) m=length($0)} END{print m}' file.txt

10. 一行式速查表

需求一行命令
搜索rg 'kw' dir/
替换全部sed -i 's/a/b/g' f
取列awk '{print $1,$NF}' f
JSON 取字段jq -r '.a.b' f
计数sort f | uniq -c
Top-Nsort | uniq -c | sort -rn | head -n
行数wc -l f
去回车tr -d '\r' < f
批量执行find ... | xargs -P4 ...
转 CSVjq -r '.[] | [.k] | @csv'

一句话记忆:grep 找、sed 改、awk 切列、jq 处理 JSON、sort+uniq 统计、xargs 批量——全部数据流,先 grep 后统计,中间不落盘。


延伸阅读

  • /regex-deep-dive/ — 工具背后的正则引擎
  • /unicode-encoding-guide/ — 编码与行尾陷阱
  • /serialization-formats-compare/ — jq 处理的 JSON 格式全景
  • /dsl-design/ — awk 本身就是一门 DSL
  • [[devops]] — 运维排查的数据流工具

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「others」更多文章

  1. 通配符与 Glob 匹配:与正则的分野与落地
  2. 算法复杂度速查:Big-O、空间复杂度与工程直觉
  3. 正则表达式深层解析:引擎、回溯与灾难性回溯