Skip to content

grep与sed

行文本过滤和批量替换是 Shell 中的高频操作,grep 负责匹配查找,sed 负责流式编辑。

一、grep 的定位

grep(Global Regular Expression Print)从文本输入中筛选匹配指定模式的行。它可以读取文件,也接受管道输入。

bash
grep "ERROR" app.log
journalctl -u nginx --since "1 hour ago" | grep "failed"

常用参数:

参数作用
-i忽略大小写
-n显示匹配行的行号
-v反向匹配——输出不包含匹配内容的行
-r递归搜索目录下的所有文件
-E使用扩展正则表达式(支持 +?| 等)
-F按普通字符串匹配(不解释正则特殊字符)
-q静默模式,不输出任何内容,只看退出码
-A N显示匹配行后面 N 行
-B N显示匹配行前面 N 行
-C N显示匹配行前后各 N 行

查错误并显示行号:

bash
grep -n "ERROR" app.log

查看错误上下文(前后 3 行):

bash
grep -C 3 "OutOfMemory" app.log

脚本中只判断是否存在匹配而不输出内容:

bash
if grep -q "ERROR" app.log; then
    echo "found error"
fi

二、普通字符串和正则

很多场景下只是查找固定的字符串,用 -F 更直接——它不解释正则特殊字符:

bash
grep -F "10.0.0.1" access.log
grep -F "[ERROR]" app.log

[.* 等字符在正则中有特殊含义,用 -F 之后就不需要逐个转义。

扩展正则用 -E

bash
grep -E "ERROR|WARN|FATAL" app.log

常用正则语法(配合 -E):

写法含义
^行首
$行尾
.匹配任意单个字符
*前一字符重复 0 次或多次
+前一字符重复 1 次或多次(需要 -E
?前一字符出现 0 或 1 次(需要 -E
[]字符集合,[0-9] 匹配一位数字
|或,多个模式取其一(需要 -E

粗略匹配 IPv4 地址:

bash
grep -E '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' access.log

这个正则只按格式过滤,不校验每段是否在 0–255 范围内。日志临筛够用,严格校验时需要更精确的工具。

三、grep 的退出码

grep 的退出码含义清晰,适合写入条件逻辑:

退出码含义
0至少找到一处匹配
1未找到任何匹配
2发生错误(文件不存在、目录无法读取等)

set -e 脚本中处理 grep 无匹配的返回值:

bash
grep "ERROR" app.log || true    # 允许 "没有匹配到" 不算致命错误

|| true 只应在确实允许为空的地方使用。盲目加会连真正的错误(如文件不存在、退出码 2)也吞掉。更稳妥的做法是用 if 包裹。

四、sed 的定位

sed(Stream Editor)按行读取、按指令处理、再输出结果。默认不改动原文件,结果打印到标准输出。

打印指定行范围:

bash
sed -n '1,5p' app.log       # 只输出第 1 到 5 行

删除空行:

bash
sed '/^$/d' app.log         # 删除匹配行,输出到屏幕,不修改原文件

替换文本:

bash
sed 's/old/new/' app.conf     # 替换每行第一个匹配
sed 's/old/new/g' app.conf    # 替换每行所有匹配(g = global)

s/old/new/ 只影响每行的第一个匹配,末尾加 g 才会替换整行中所有出现的 old。

五、sed 的行地址

sed 命令前可以加"地址"来限制只在匹配的行上执行操作:

地址写法含义
1p第 1 行
1,10p第 1 到 10 行
/ERROR/p包含 ERROR 的行
$p最后一行

只看第 10 到 20 行:

bash
sed -n '10,20p' app.log

只在匹配行上执行替换:

bash
sed '/server_name/s/example.com/api.example.com/' nginx.conf

这个写法先找到包含 server_name 的行,再在这行里把 example.com 替换成 api.example.com。sed 不识别配置文件的结构语义——它只做纯文本匹配。多层缩进的 YAML、JSON 配置不适合用 sed 深度修改。

六、原地修改

Linux 上 sed 的 -i 参数支持"原地修改"文件(而不是输出到屏幕)。

bash
sed -i.bak 's/listen 80/listen 8080/' nginx.conf

-i.bak 表示修改原文件前,先备份为 nginx.conf.bak。裸用 sed -i 不留备份,生产线操作风险较高。

批量替换目录中的多个文件:

bash
find /etc/nginx -type f -name "*.conf" -print0 |
while IFS= read -r -d '' file; do
    sed -i.bak 's/old.example.com/new.example.com/g' "$file"
done

批量修改配置后,马上跑服务自带的语法检查(如 nginx -tsshd -t)确认没有语法错误再继续。

七、常见文本处理模式

去掉注释行和空白行:

bash
grep -v '^[[:space:]]*#' app.conf | grep -v '^[[:space:]]*$'

[[:space:]] 是 POSIX 字符类,匹配空格和 Tab,比 \s 跨平台兼容性更好。

替换整行配置(匹配到行后整行覆盖):

bash
sed -i.bak 's/^worker_processes.*/worker_processes auto;/' nginx.conf

在匹配行后面追加一行内容:

bash
sed '/http {/a\    server_tokens off;' nginx.conf

多个 sed 操作用分号分隔,也可以写多个 -e。逻辑变复杂时写成独立的 sed 脚本文件,比一行长命令更便于维护和回滚。

八、使用边界

grep 和 sed 适合的场景:

场景工具
从日志中过滤关键字grep
查看匹配行的上下文grep -C
简单替换一行配置sed
批量删除注释和空行sed / grep
批量文本替换sed -i.bak

不适合的场景和替代方案:

场景推荐工具
修改 JSON 结构jq
修改 YAML 配置yq 或配置管理工具
解析多层嵌套的日志字段awk、Python 或日志平台
涉及上下文状态的复杂文本处理Python、Go 等通用语言

grep 和 sed 是单行处理工具,不关心上下文结构。当需要理解文件语法(缩进、嵌套、引用关系)时,用能解析该格式的专用工具会更准确。