44·Shell 脚本进阶高级

sed 高级替换与多行处理

sedregex

第44课:sed 高级替换与多行处理

学习目标

通过本课的学习,你将能够:

  1. 掌握 sed 替换命令中常用标志(flags)和修饰符的用法,实现大小写转换、反向引用等高级替换。
  2. 理解并运用 sed 的多行处理命令(如 N, P, D),处理跨越多行的文本模式。
  3. 学会使用 sed 的分支和条件执行命令(b, t),构建更复杂的文本处理逻辑。
  4. 能够结合正则表达式与上述高级功能,解决实际运维脚本中复杂的文本处理需求。

核心概念

在掌握了 sed 的基本替换(s)后,我们将进入更强大的高级领域。可以把 sed 想象成一个精密的文本工厂流水线。

1. 高级替换标志与修饰符 基本替换 s/pattern/replacement/ 后面可以附加一些“开关”来改变行为。

  • g (global):这是最常用的标志,替换一行中的所有匹配项,而不仅仅是第一个。
  • iI (ignore case):让匹配忽略大小写(例如,s/foo/bar/i)。
  • 数字标志:如 s/pattern/replacement/2,表示只替换一行中的第2个匹配项。
  • e (exec):一个强大但慎用的标志,它会将 replacement 部分的输出作为 Shell 命令来执行,并将命令的输出替换回原位。

2. 多行处理命令 默认 sed 是逐行处理的。要处理多行,需要特殊命令将下一行“拉”到当前行。

  • N 命令 (Next):将下一行读入模式空间(pattern space),与当前行用换行符 \n 连接。这样模式空间就有两行内容了。
  • P 命令 (Print):打印模式空间中第一个换行符(\n)之前的内容。
  • D 命令 (Delete):删除模式空间中第一个换行符(\n)之前的内容,然后重新开始循环(不再读取新行)。
  • s 命令的 m 修饰符 (multiline):当与 s 替换一起使用时,让 ^$ 锚定匹配模式空间中的每一行,而不仅仅是整个模式空间的开头和结尾。

3. 分支与执行流控制

  • b label (branch):无条件跳转到脚本中的 :label 标签处继续执行。类似编程中的 goto
  • t label (test):如果自上次读取行或执行 t 命令以来,已经成功执行了 s 替换,则跳转到 :label。这是基于替换结果的条件跳转。

代码示例

让我们通过一些具体例子来理解这些概念。

示例1:不区分大小写与全局替换

# 生成一个测试文件
echo -e "Hello World\nHELLO sed\nhello linux" > test.txt

# 基础替换:只替换每行第一个匹配,且区分大小写
sed 's/hello/Hi/' test.txt
# 输出:
# Hi World   # 替换了第一个 ‘hello’
# HELLO sed  # 没匹配到(大写H)
# Hi linux   # 替换了第一个 ‘hello’,但注意原行是 ‘hello’,所以替换为 ‘Hi linux’

# 使用 `ig` 标志:不区分大小写,并替换每行所有匹配
sed 's/hello/Hi/ig' test.txt
# 输出:
# Hi World   # ‘Hello’ 被替换
# Hi sed     # ‘HELLO’ 被替换
# Hi linux   # ‘hello’ 被替换

示例2:多行处理 - 连接并替换跨越两行的模式 假设我们想把 Start 和紧接着的 Stop 替换为 BeginEnd

# 创建测试文件
cat > multi.txt << EOF
This is the Start
of a block. Stop it.
Another line.
EOF

# 使用 N 将下一行读入,然后用 s 进行跨行替换
sed '/Start/ { N; s/Start\nof a block\. Stop/Begin\nof a block. End/ }' multi.txt
# 解释:
# 1. `/Start/` 匹配包含“Start”的行。
# 2. `{ N; ... }` 执行一个代码块。
# 3. `N` 把下一行(`of a block. Stop it.`)追加到当前行后面,模式空间变成 "Start\nof a block. Stop it."
# 4. `s/Start\nof a block\. Stop/Begin\nof a block. End/` 对这个多行模式空间进行替换。
# 输出:
# This is the Begin
# of a block. End it.
# Another line.

示例3:使用 t 命令进行迭代替换 我们想把连续多个空行压缩成一个空行。

# 创建测试文件
echo -e "Line 1\n\n\n\nLine 2" > blank.txt

# 使用 `t` 和 `N` 进行迭代压缩
sed ':loop; /^$/N; s/^\(\n\)/\1/; t loop' blank.txt
# 解释:
# 1. `:loop` 定义一个标签。
# 2. `/^$/N` 如果当前行是空行,则执行 `N` 读入下一行。模式空间变成 `\n<下一行内容>`。
# 3. `s/^\(\n\)/\1/` 尝试替换:如果模式空间以多个 `\n` 开头(即连续空行),则匹配成功。这里用 `\1` 替换自身,其实什么也没做,**关键是让 `s` 命令执行成功**。
# 4. `t loop` 因为上一个 `s` 命令成功了(匹配到了多个换行符),所以跳转回 `:loop`,继续检查并读取下一行,直到遇到非空行或文件末尾。
# 最终输出会将连续多个空行变成一个空行。
# 输出:
# Line 1
#
# Line 2

实践练习

练习1:基础高级替换 创建一个文件 user_info.txt 内容如下:

Name: Alice
Email: [email protected]
Name: Bob
Email: [email protected]
Name: Charlie
Email: [email protected]

请写一个 sed 命令,将所有域名(@ 后面的部分)统一转换为小写。 预期输出

Name: Alice
Email: [email protected]
Name: Bob
Email: [email protected]
Name: Charlie
Email: [email protected]

练习2:多行处理 文件 config.cfg 内容如下:

[server]
host = 192.168.1.1
port = 8080
# This is a comment
database = mysql

请使用 sed 删除文件中所有跨多行的注释块(以 # 开头的行),但保留单行的配置。 预期输出

[server]
host = 192.168.1.1
port = 8080
database = mysql

提示:你需要处理 # This is a comment 这样的单行注释,以及可能的多行 # ... 块。

练习3:综合应用 文件 data.csv 内容如下:

ID,Value,Status
1,100,Active
2,200,Inactive
3,300,Active
4,400,Error
5,500,Active

请写一个 sed 命令,在文件开头插入一行标题 # Last Updated: 2023-10-27,并将所有 StatusActive 的行的 Value 字段增加 10预期输出

# Last Updated: 2023-10-27
ID,Value,Status
1,110,Active
2,200,Inactive
3,310,Active
4,400,Error
5,510,Active

提示:这需要结合地址匹配、反向引用(\(...\))和计算替换。

常见错误

  1. 忘记转义特殊字符:在 patternreplacement 中使用 .*[ 等正则字符或 /&sed 特殊字符时,忘记用 \ 进行转义。例如,匹配 http:// 应写为 http:\/\/
  2. N 命令处理文件末尾:当 N 命令在最后一行执行时,因为没有下一行可读,sed 会直接结束。有时这会导致预期中的最后一行未被处理。可以在脚本中通过更复杂的地址或标签逻辑来处理。
  3. 误解 s 命令中的 m 修饰符m 修饰符主要影响 ^$ 的行为,在单行模式空间中,m 没有实际意义。只有在使用 N 等命令创建了包含多行(以 \n 分隔)的模式空间后,s/.../.../gm 中的 m 才会让 ^$ 匹配每一行的开头和结尾。
  4. 过度依赖 bt 造成死循环:使用分支命令时,如果没有正确的跳出条件(如 d 删除或到达文件末尾),可能导致脚本在一个标签处无限循环。务必确保循环逻辑有终止条件。

小结

本课我们深入探索了 sed 的高级功能:

  • 高级替换:通过 g, i, 数字等标志,以及 e 执行标志,实现了更灵活、更强大的文本替换。
  • 多行处理:学习了使用 N 命令将行连接起来,以及配合 P, Dsm 修饰符来处理跨行文本,这是进行复杂配置文件编辑的利器。
  • 流程控制:掌握了 bt 命令,可以实现循环和条件执行,让 sed 脚本能处理更复杂的逻辑。 这些高级技巧使得 sed 不仅仅是简单的查找替换工具,而是能够高效处理结构化文本数据的强大流编辑器,是 Shell 脚本进阶的必备技能。在下一课我们将深入学习正则表达式,它是 sed 命令的核心引擎,学好它将让你如虎添翼。

练习编辑器

rust
Loading...

继续学习

完成本课后,建议继续学习下一课「正则表达式详解」 以巩固所学知识。