sed默认仅替换每行第一个匹配项,这是POSIX标准定义的设计行为;加g标志才全局替换,数字修饰符可指定替换第几次出现,^锚点用于行首匹配。

别用“教程”这个词——sed 和 awk 不是靠背命令学会的,而是靠修日志、改配置、扒数据时反复试错练出来的。真正卡住你的从来不是语法,而是“该用哪个”“为什么这行没生效”“备份在哪”。下面直奔实操场景。
sed 替换时为什么只替换了第一个匹配?
因为默认 s/old/new/ 只处理每行**第一次出现**的 old。这不是 bug,是设计:它让你能精准控制替换粒度。
- 加
g标志才全局替换:sed 's/ERROR/error/g' app.log - 想只换第 2 次出现?写数字:
sed 's/timeout/TIMEOUT/2' access.log - 想换行首的
#但不碰注释里的#?用锚点:sed 's/^#//'(注意^必须紧贴s的 pattern 开头) - 误用了
/当分隔符又想替换路径(如/usr/bin),会触发“too many slashes”错误——换分隔符:sed 's|/usr/bin|/opt/bin|g'
awk 处理日志时怎么按列取 IP 和状态码?
关键不是“怎么写”,而是“字段怎么切”。默认空格/制表符分隔,但 Nginx 或 Apache 日志常用双引号、方括号、空格混搭,直接 很可能取错。
Linux系统管理专家,覆盖12大模块:用户权限、SSH、存储、网络、systemd、防火墙、日志监控、备份恢复、TLS证书、Ansible、容器、IaC。提供配置、验证、加固、监控、备份、自动化、故障排查、回滚闭环。关键词:useradd、sudo、sshd_config、chmod、SEL...
- 先看真实分隔:用
head -1 access.log | cat -A查看不可见字符(比如^I是 tab) - 用
-F显式指定分隔符:awk -F' ' '{print $1, $9}' access.log(空格分隔) - 正则当分隔符更稳:
awk -F'[[:space:]]+"?[^"]+"?' '{print $1, $9}'(慎用,调试成本高) - 更可靠的做法是用
match()提取:awk '{match($0, /([0-9.]+) -.*" [0-9]{3} /, arr); print arr[1], substr($0, RSTART+RLENGTH-4, 3)}'(适合固定格式日志)
sed -i 直接改文件后发现改错了,还能恢复吗?
不能——除非你提前加了备份后缀。Linux 不像编辑器有 undo,sed -i 是原子覆盖,出错就是永久丢失。
- 永远带备份:
sed -i.bak 's/foo/bar/g' config.conf→ 生成config.conf.bak - 测试阶段禁用
-i,先确认输出正确:sed 's/foo/bar/g' config.conf | head -5 - 如果已经没备份,且文件还在被进程占用(如日志),可尝试从
/proc/PID/fd/下找回原始内容(需 root 权限) -
git管理的配置文件?改之前git stash或git commit -n留痕
什么时候必须用 awk 而不是 sed?
当你需要跨行计算、条件判断、或基于字段值做逻辑分支时,sed 就力不从心了。它没有变量、没有 if、不能累加——这些不是“高级功能”,而是基础能力缺口。
- 统计每个 IP 访问次数:
awk '{ip[$1]++} END{for (i in ip) print i, ip[i]}' access.log - 只打印响应时间 > 1000ms 的行:
awk '$NF > 1000 {print}' access.log(假设最后一列是毫秒) - 合并多行日志(如 Java 异常堆栈):
awk '/^Exception/{if (buf) print buf; buf=$0; next} /^$/ {next} {buf = buf "\n" $0} END{print buf}' - 想把 CSV 第三列全转大写?
sed做不到字段级操作,awk -F, -v OFS=, '{$3 = toupper($3); print}' data.csv
最常被忽略的一点:awk 的 NR 和 FNR 差异。处理多个文件时,NR 是总行号,FNR 是当前文件行号——漏掉这个,跨文件统计就全乱。

















