1. 初识sed:Linux文本处理的瑞士军刀
第一次接触sed是在处理一个500MB的日志文件时,当时我需要把里面所有的"ERROR"替换成"CRITICAL"。用文本编辑器打开直接卡死,vim也力不从心,直到同事扔给我一行sed命令:sed 's/ERROR/CRITICAL/g' access.log > new.log,三秒钟搞定。那一刻我意识到,这个1974年就诞生的老古董工具,在现代Linux系统管理中依然是无可替代的利器。
sed(Stream EDitor)的本质是一个非交互式的行编辑器,它逐行处理输入流(文件或管道输入),根据编辑指令修改内容后输出。与vim这类交互式编辑器不同,sed的所有操作都是通过命令行参数预设好的,这种特性让它特别适合:
- 自动化脚本中的文本处理
- 批量修改配置文件
- 日志文件实时处理
- 配合管道实现复杂文本转换
提示:sed默认不会修改原文件,所有操作结果都是输出到标准输出。要修改原文件必须使用-i选项(后面会详细说明)
2. 替换操作:从基础到高阶的全面掌握
2.1 基础替换语法剖析
sed最常用的s命令(substitute)基本结构如下:
bash复制sed 's/pattern/replacement/flags' filename
这里的三个斜杠/是分隔符,实际上可以用任何单个字符代替(当pattern包含斜杠时特别有用)。比如处理URL时:
bash复制sed 's|http://|https://|g' urls.txt
2.1.1 匹配规则详解
pattern支持基本正则表达式(BRE)- 特殊字符需要转义:
\.表示真实点号,\*表示星号 ^匹配行首,$匹配行尾
示例:将每行开头的空格替换为制表符
bash复制sed 's/^[[:space:]]\+/\t/' indent.txt
2.2 全局替换与局部替换对比
不加任何flag时,sed只会替换每行第一个匹配项:
bash复制echo "apple apple apple" | sed 's/apple/Orange/'
# 输出:Orange apple apple
添加gflag实现全局替换:
bash复制echo "apple apple apple" | sed 's/apple/Orange/g'
# 输出:Orange Orange Orange
更精细的控制方式:
- 数字flag:只替换第N个匹配项
bash复制echo "1 2 3 1 2 3" | sed 's/2/Two/2' # 输出:1 2 3 1 Two 3 pflag:打印发生替换的行(需配合-n选项)bash复制sed -n 's/error/ERROR/p' logfile
2.3 高级替换技巧
2.3.1 反向引用
用\(\)分组后,可以在replacement部分用\1~\9引用:
bash复制echo "Hello World" | sed 's/\(Hello\) \(World\)/\2 \1/'
# 输出:World Hello
2.3.2 大小写转换
GNU sed扩展:
\L:后续内容转小写\U:后续内容转大写\E:结束转换
示例:将第二个单词转为大写
bash复制echo "hello world" | sed 's/\([^ ]* \)\([^ ]*\)/\1\U\2/'
# 输出:hello WORLD
3. 删除操作:精准控制文本内容
3.1 按行号删除
基本语法:
bash复制sed '[address]d' filename
实用示例:
bash复制# 删除第3行
sed '3d' config.ini
# 删除3-5行
sed '3,5d' config.ini
# 删除最后一行
sed '$d' data.csv
# 删除空白行(包括含空格的行)
sed '/^[[:space:]]*$/d' document.txt
3.2 模式匹配删除
bash复制# 删除包含"DEBUG"的行
sed '/DEBUG/d' application.log
# 删除不含"ERROR"的行(相当于grep -v)
sed '/ERROR/!d' system.log
# 删除从"START"到"END"之间的内容
sed '/START/,/END/d' template.txt
注意:模式匹配默认是贪婪匹配。比如
/START/,/END/会从第一个START到第一个END,如果要匹配最远的END需要:/START/,${/END/!d;}
4. 插入与修改:动态编辑文本流
4.1 行编辑操作符
| 命令 | 作用 | 示例 |
|---|---|---|
i |
在指定行前插入 | sed '2i\inserted line' file |
a |
在指定行后追加 | sed '/pattern/a\new line' file |
c |
替换整行内容 | sed '5c\replaced content' file |
4.2 多行插入技巧
插入多行内容时,可以用\n换行:
bash复制sed '3i\line1\nline2\nline3' data.txt
或者使用读取文件方式:
bash复制sed '/pattern/r insert.txt' target.txt
5. 地址定位:精准操作的关键
5.1 行号定位进阶
bash复制# 每隔2行操作一次(第1,3,5...行)
sed '1~2s/^/# /' config.txt
# 操作最后3行
sed 'N;$!D;$s/error/ERROR/g' logfile
5.2 模式范围的高级用法
bash复制# 从包含"START"的行到第10行
sed '/START/,10s/old/new/g' doc.md
# 两个模式之间的非连续范围
sed '/BEGIN/{:a;N;/END/!ba;s/foo/bar/g}' data.txt
6. 实战案例:从日志处理到配置管理
6.1 日志处理三板斧
bash复制# 提取最近1小时内的ERROR日志
sed -n "/^$(date -d '1 hour ago' '+%Y-%m-%d %H')/,/^$(date '+%Y-%m-%d %H')/p" system.log | grep ERROR
# 给日志添加时间戳(当原始日志缺失时)
sed "s/^/$(date '+%Y-%m-%d %H:%M:%S') /" application.log
# 统计每种错误类型出现次数
sed -n 's/.*\(ERROR [A-Z_]\+\):.*/\1/p' error.log | sort | uniq -c
6.2 配置文件批量修改
bash复制# 安全修改 - 先备份再修改
sed -i.bak 's/^#\?Port 22/Port 2222/' /etc/ssh/sshd_config
# 多条件修改
sed -e 's/^Timeout=.*/Timeout=300/' \
-e 's/^MaxClients=.*/MaxClients=100/' \
service.conf
6.3 数据格式转换
CSV转TSV:
bash复制sed 's/,/\t/g' data.csv > data.tsv
JSON美化(简易版):
bash复制sed 's/[{},]/\0\n/g; s/":/": /g' compact.json
7. 性能优化与排错指南
7.1 处理大文件时的技巧
bash复制# 流式处理避免内存问题
cat huge.log | sed 's/pattern/replacement/' > new.log
# 只处理文件前N行(用于测试)
head -1000000 bigfile | sed '...' > sample.out
7.2 常见问题排查
-
特殊字符未转义:
bash复制# 错误示例(点号需要转义) sed 's/192.168.1.1/localhost/' file # 正确写法 sed 's/192\.168\.1\.1/localhost/' file -
贪婪匹配问题:
bash复制# 获取引号内的内容(非贪婪匹配) echo '"foo" and "bar"' | sed 's/"[^"]*"/X/' # 输出:X and X -
跨行匹配技巧:
bash复制# 将多行合并为一行处理 sed ':a;N;$!ba;s/\n//g' multiline.txt
8. 我的sed工具箱
这些是我多年积累的实用命令片段:
bash复制# 删除Markdown文件中的注释
sed '/^<!--.*-->$/d;/^<!--/,/^-->/d' doc.md
# 给配置文件添加节标题
sed -i '/^\[section\]/i # ====== SECTION START ======' config.ini
# 生成随机密码(sed版)
tr -dc A-Za-z0-9 < /dev/urandom | head -c32 | sed 's/./&\n/g' | sed -n '/./{p;q}' | tr -d '\n'
掌握sed就像获得了一把文本处理的万能钥匙。刚开始可能会觉得语法晦涩,但一旦熟悉了它的模式,你会发现自己越来越习惯用sed来解决问题。我现在的习惯是:任何需要超过3次重复操作的文本处理任务,都会考虑写成sed命令或脚本。