Linux sed命令的高级玩法:从入门到精通的文本流编辑技巧

在Linux系统中,sed(Stream Editor,流编辑器)是一款功能强大的文本处理工具,它以行为单位对文本流进行编辑,广泛用于日志分析、配置文件修改、数据清洗等场景。相较于基础的查找替换,sed的高级特性(如多 line 处理、 hold space 操作、正则表达式高级用法等)能解决更复杂的文本处理问题。本文将深入探讨sed的高级玩法,帮助读者掌握从“简单替换”到“复杂文本流控制”的核心技巧。

目录#

  1. sed基础回顾:核心概念与工作原理
  2. 高级模式匹配:正则表达式进阶
  3. 多line处理:突破单行限制
  4. hold space玩转:临时缓冲区的高级应用
  5. 高级替换技巧:flags与命令组合
  6. 实战场景:脚本化与批量处理
  7. 最佳实践与避坑指南
  8. 参考资料

1. sed基础回顾:核心概念与工作原理#

在深入高级用法前,先快速回顾sed的核心机制,这是理解高级技巧的基础。

1.1 工作流程#

sed的工作流程可概括为**“读取-处理-输出”**三步:

  1. 读取(Read):从输入流(文件或管道)中读取一行文本,存入pattern space(模式空间,临时缓冲区)。
  2. 处理(Process):根据用户指定的命令对pattern space中的内容进行编辑。
  3. 输出(Print):处理完成后,将pattern space中的内容输出到标准输出(默认行为,可通过-n选项禁用)。

重复以上步骤,直到所有行处理完毕。

1.2 核心缓冲区#

sed有两个关键缓冲区:

  • pattern space:默认工作区,存放当前处理的行。
  • hold space:“备用缓冲区”,用于临时存储数据,可与pattern space交换数据(高级玩法的核心)。

1.3 基础命令速览#

命令作用示例
s/pattern/replacement/替换patternreplacementsed 's/old/new/' file
d删除当前行sed '/^#/d' file(删除注释行)
p打印当前行(需配合-nsed -n '/error/p' file(打印含error的行)
n读取下一行到pattern spacesed 'n;d' file(删除偶数行)

2. 高级模式匹配:正则表达式进阶#

sed支持基本正则表达式(BRE)和扩展正则表达式(ERE),通过-E选项启用ERE后,可使用更丰富的语法(如+?|、分组等)。

2.1 扩展正则表达式(ERE)的常用语法#

语法作用示例
+匹配前一个字符至少1次sed -E 's/ab+/X/'(将ababb等替换为X
?匹配前一个字符0或1次sed -E 's/colou?r/color/'(匹配colorcolour
``逻辑“或”
()分组与捕获sed -E 's/(\w+)@(\w+)\.com/\2@\1.com/'(交换邮箱用户名和域名)
{n,m}匹配前一个字符n到m次sed -E 's/[0-9]{3,4}/XXX/'(替换3-4位数字为XXX)

2.2 锚点与边界匹配#

  • ^:行首锚点(如/^Error/匹配以Error开头的行)。
  • $:行尾锚点(如/done$/匹配以done结尾的行)。
  • \b:单词边界(如/\bhello\b/匹配独立的“hello”单词,不匹配“helloworld”)。

示例:提取日志中以[ERROR]开头且包含timeout的行:

sed -n -E '/^\[ERROR\].*timeout/p' app.log

3. 多line处理:突破单行限制#

sed默认按行处理文本,但实际场景中(如跨 line 日志、段落合并)需处理多行。核心命令包括N(合并行)、P(打印部分行)、D(删除部分行)。

3.1 合并多行:N命令#

N(Next)命令将下一行文本追加到pattern space,两行之间用换行符分隔。

场景:处理以反斜杠\结尾的折行(如配置文件中的长行)。
示例:将折行合并为单行:

# 输入文件(file.txt):
line1 part1 \
line1 part2
line2
 
# 合并命令:
sed '/\\$/ { N; s/\\\n// }' file.txt
 
# 输出:
line1 part1 line1 part2
line2
  • /\\$/:匹配以\结尾的行。
  • N:追加下一行到pattern space
  • s/\\\n//:删除\和换行符,实现合并。

3.2 多行删除:D命令#

D(Delete up to newline)命令删除pattern space中第一个换行符前的内容,若剩余内容非空,不读取新行,直接重新处理当前pattern space

场景:删除连续空行(保留一个空行)。
示例

# 输入文件(file.txt):
line1
 
line2
 
 
line3
 
# 去重空行命令:
sed '/^$/ { N; /^\n$/ D }' file.txt
 
# 输出:
line1
 
line2
 
line3
  • /^$/:匹配空行。
  • N:追加下一行到pattern space(此时pattern space\n\n)。
  • /^\n$/ D:若两行都是空行(即\n\n),删除第一行空行,剩余\n,再次进入循环处理(此时pattern space\n,不再匹配/^$/,输出单个空行)。

3.3 多行打印:P命令#

P(Print up to newline)命令打印pattern space中第一个换行符前的内容,常用于提取跨 line 内容。

场景:提取startend标记之间的多行文本(含标记)。
示例

# 输入文件(file.txt):
aaa
start
bbb
ccc
end
ddd
 
# 提取命令:
sed -n '/start/,/end/ { P; n; P }' file.txt
 
# 输出:
start
bbb
ccc
end
  • /start/,/end/:匹配从startend的行范围。
  • P:打印当前行(start)。
  • n:读取下一行(bbb)。
  • P:打印下一行,循环直到end

4. hold space玩转:临时缓冲区的高级应用#

hold spacesed的“隐藏武器”,通过与pattern space的数据交换,可实现复杂的文本重组(如行反转、块提取)。核心命令如下:

命令作用
hpattern space内容覆盖到hold space
Hpattern space内容追加到hold space(换行分隔)
ghold space内容覆盖到pattern space
Ghold space内容追加到pattern space(换行分隔)
x交换pattern spacehold space内容

4.1 行反转:逆序输出文件内容#

场景:将文件行序从尾到头输出(类似tac命令)。
原理:用H累加所有行到hold space,最后用g取出并打印。
命令

sed -n 'H; $ { g; s/\n//; p }' file.txt
  • H:每读取一行,追加到hold space(格式:\nline1\nline2...)。
  • $:仅处理最后一行时执行后续命令。
  • g:将hold space内容覆盖到pattern space
  • s/\n//:删除开头的空行(因H首次追加时会添加\n)。
  • p:打印结果。

4.2 块提取:提取两个标记间的内容并处理#

场景:提取BEGINEND之间的块,去除标记后输出。
命令

# 输入文件(file.txt):
aaa
BEGIN
bbb
ccc
END
ddd
 
# 提取命令:
sed -n '/BEGIN/{:a; N; /END/!ba; s/BEGIN\n//; s/\nEND//; p}' file.txt
 
# 输出:
bbb
ccc
  • /BEGIN/:匹配BEGIN行。
  • :a:定义标签a(循环入口)。
  • N:追加下一行到pattern space
  • /END/!ba:若未匹配END,跳转到标签a(继续追加下一行)。
  • s/BEGIN\n//; s/\nEND//:删除BEGINEND标记。
  • p:打印提取的块。

5. 高级替换技巧:flags与命令组合#

seds命令支持多种flags(修饰符),结合其他命令可实现复杂替换逻辑。

5.1 常用替换flags#

flag作用示例
g全局替换(默认仅替换第一个匹配)sed 's/old/new/g'
i忽略大小写sed 's/Error/info/i'
p打印替换后的行(需配合-nsed -n 's/old/new/p'
w file将替换结果写入文件sed 's/error/warn/w log.txt'
e将替换结果作为命令执行(GNU sed特有)sed 's/^file: $.*$/ls -l \1/e'(执行ls -l 文件名

5.2 组合命令:条件替换与循环#

场景:对包含error的行,将行首添加[CRITICAL]并替换errorERROR
命令

sed '/error/ { s/^/[CRITICAL] /; s/error/ERROR/g }' file.txt
  • /error/:仅对含error的行执行花括号内命令。
  • 分号分隔两个s命令,实现多步替换。

5.3 反向引用:用\n复用匹配内容#

s命令中,\1\2等表示正则分组捕获的内容(需用-E启用ERE)。

场景:交换日期格式(YYYY-MM-DDDD/MM/YYYY)。
命令

echo "2023-10-05" | sed -E 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3\/\2\/\1/'
# 输出:05/10/2023

6. 实战场景:脚本化与批量处理#

对于复杂逻辑,可将sed命令写入脚本文件(.sed),提高可维护性。

6.1 编写sed脚本#

示例clean_log.sed(清洗日志文件,保留ERROR行并格式化时间):

#!/usr/bin/sed -f
 
# 删除空行
/^$/d
 
# 保留ERROR行,其他行删除
/ERROR/!d
 
# 格式化时间(YYYY-MM-DD HH:MM:SS → MM/DD HH:MM)
s/^([0-9]{4})-([0-9]{2})-([0-9]{2}) ([0-9]{2}:[0-9]{2}):[0-9]{2}/\2\/\3 \4/
 
# 替换ERROR为【错误】
s/ERROR/【错误】/g

执行脚本

chmod +x clean_log.sed
./clean_log.sed app.log > cleaned.log

6.2 批量处理多个文件#

使用find结合sed批量修改文件(需谨慎,建议先备份):

# 批量替换所有.txt文件中的"old"为"new",并创建.bak备份
find . -name "*.txt" -exec sed -i.bak 's/old/new/g' {} +
  • -i.bak:修改文件并创建.bak备份(处理完成后可删除.bak)。

7. 最佳实践与避坑指南#

7.1 测试先行,避免直接修改#

使用-np预览效果,确认无误后再执行修改:

# 预览替换结果,不修改文件
sed -n 's/old/new/p' file.txt

7.2 处理特殊字符#

  • 若替换内容含/,可改用其他分隔符(如|#):
    sed 's|/usr/local|/opt|g' file.txt  # 用|作为分隔符
  • 转义特殊字符(如$*\):
    sed 's/\$HOME/\/home/g' file.txt  # 替换$HOME为/home

7.3 性能优化#

  • 避免不必要的全局替换(g flag),仅在需要时使用。
  • 对大文件,优先使用地址范围限制处理行数:
    sed -n '1,1000 s/old/new/p' large_file.txt  # 仅处理前1000行

7.4 局限性:不适合嵌套结构#

sed是行 oriented 工具,不擅长处理嵌套结构(如XML/HTML),此类场景建议使用xmllintawk

8. 参考资料#

通过本文的高级技巧,相信你已能应对大部分复杂文本处理场景。sed的强大之处在于其灵活性,结合正则表达式和缓冲区操作,可实现从简单替换到复杂流控制的全流程文本编辑。实践是掌握的关键,建议结合实际需求多动手尝试!