搜”命令行批量替换文本”,答案里有的用 sed,有的用 awk,看着好像都能解决问题,但不知道自己该学哪个、什么场景该用哪个。两者确实经常一起出现在同一批搜索结果里——但它们解决的其实是不同维度的问题:sed 面向”行”做替换、删除、插入;awk 面向”列”做结构化处理。搞清楚这个区别,选工具就不用再猜了。

这是 Linux 命令系列的一篇,承接 Linux 常见命令与工具地图

sed 基本用法:按行处理文本

sed(stream editor)最核心的命令是 s,替换文本:

1
2
3
4
5
sed 's/old/new/' file.txt
# 把每一行里第一个匹配到的 "old" 替换成 "new",只替换每行的第一处

sed 's/old/new/g' file.txt
# 加上 g(global),一行里所有匹配到的 "old" 都替换,不只是第一个

默认情况下 sed 不会修改原文件,只是把处理结果打印到标准输出。想直接改文件本身,要加 -i(in-place)——这里有个必须提前知道的跨平台坑:

1
2
3
4
5
6
7
8
9
# macOS / BSD sed
sed -i '' 's/old/new/' file.txt
# BSD sed 要求 -i 后面必须紧跟一个备份文件后缀参数,不想要备份就传一个空字符串 ''
# 漏了这个空字符串参数,sed 会把下一个参数(也就是替换脚本本身)当成后缀,直接报错

# GNU sed(Linux 发行版默认自带的 sed)
sed -i 's/old/new/' file.txt
# GNU sed 的 -i 不需要额外参数,直接原地修改,不生成备份
# 想要备份就不带空格直接写后缀:sed -i.bak 's/old/new/' file.txt,会生成 file.txt.bak

同一条 sed -i 命令,在 macOS 上能跑通,搬到 Linux 服务器上可能报错(或者反过来)——写跨平台脚本时这是最容易被忽略的细节。

除了替换,sed 还能按行号删除或者操作某个范围:

1
2
3
4
5
6
7
8
sed '2d' file.txt
# 删除第 2 行(打印到标准输出,加 -i 才会真正改文件)

sed '2,4d' file.txt
# 删除第 2 到第 4 行(含首尾)

sed -e 's/foo/bar/' -e 's/baz/qux/' file.txt
# -e:一次执行多个替换表达式,不用为每个替换单独跑一次 sed

awk 基本用法:按列处理结构化文本

awk 处理的思路不一样——它默认把每一行按空白字符拆成若干”字段”,用 $1$2 这些变量取某一列:

1
2
3
echo "alice 25 engineer" | awk '{print $1}'
# $1 是第一个字段,输出 alice
# $0 是整行原始内容,$2、$3 依次是第二、第三个字段

几个内置变量,日常最常用:

1
2
3
4
5
6
awk '{print NF}' file.txt
# NF:当前这一行有多少个字段(Number of Fields)

awk '{print NR, $0}' file.txt
# NR:到目前为止已经处理了多少行(Number of Records),是所有输入累计的行号
# 注意 NR 跨文件也是累计的——如果同时处理多个文件,"当前文件内的行号"要用 FNR,不是 NR

按条件过滤是 awk 真正擅长的场景:

1
2
3
4
5
6
7
8
9
10
11
awk '$3 > 100 {print $1}' data.txt
# 只有第 3 列的值大于 100 的行,才输出这一行的第 1 列
# 条件写在 {} 前面,满足条件的行才会执行后面 {} 里的动作

awk 'BEGIN {print "开始处理"} {sum += $2} END {print "总计:", sum}' data.txt
# BEGIN 块:在处理任何一行之前执行一次,常用来打印表头或者初始化变量
# END 块:在处理完所有行之后执行一次,常用来输出汇总统计结果
# 中间的主体部分对每一行都执行一次,这里是把第 2 列累加进 sum

awk -F',' '{print $2}' data.csv
# -F:指定字段分隔符,默认是空白字符,处理 CSV 这类逗号分隔的文件要显式指定

同一个任务,两种工具的差距在哪

假设有一份访问日志,每行是”路径 响应时间(ms)”,想找出响应时间超过 500ms 的所有路径。用 awk 几乎是原生支持:

1
2
awk '$2 > 500 {print $1}' access.log
# 一行搞定:按第 2 列的数值判断,满足条件就输出第 1 列

sed 做同样的事,会很别扭——sed 的正则匹配本质是”文本模式匹配”,不理解”这一列的值是不是大于 500”这种数值比较,得先把数值范围拆成一堆正则模式去匹配文本形式,写出来又长又脆弱,稍微换一种数字格式就得重新调整正则。这不是 sed 的缺陷,是它压根不是为这类场景设计的——sed 面对的是”文本”,awk 面对的是”字段和数值”。

该用哪个:判断标准

  • 只是替换、删除、插入某几行文本,不涉及”按列取值”或者”数值比较”——用 sed,更直接
  • 需要”取第几列””按某一列的值筛选””跨行做统计汇总”——用 awk,这是它的设计初衷
  • 两者也能组合使用,比如先用 awk 提取出需要的列,再用 sed 调整输出格式,但没必要为了”只用一个工具”硬凑一个别扭的写法

写到这里

sedawk 都是几十年历史的经典 Unix 工具,功能远不止本文列的这些(sed 支持完整的模式空间和保持空间操作,awk 本身就是一门小型编程语言,支持自定义函数、数组),但日常排查和批处理,本文这些用法已经能覆盖大部分场景。选错工具通常不是因为哪个”更强”,而是没看清任务本质是”处理行”还是”处理列”。