Sed 管理模式
模式空间与暂存空间进阶管理
Section titled “模式空间与暂存空间进阶管理”除了基本的逐行处理,sed 还提供了强大的命令来操作其内部缓冲区:模式空间 (pattern space) 和暂存空间 (hold space)。理解这些命令(n、N、h、H、g、G、x、p、P、d、D)将释放 sed 进行复杂多行操作、文本重排以及基于前一行进行条件处理的能力。
n 命令:处理下一行
Section titled “n 命令:处理下一行”小写的 n 命令从根本上改变了 sed 对当前行的正常流程。它的动作包括:
- 打印模式空间的当前内容(除非默认输出被
-n选项或之前的d命令抑制)。 - 清空模式空间。
- 将输入流的 下一 行读入当前为空的模式空间。如果没有更多输入行,
sed终止。 - 对模式空间中 新读入 的这一行执行脚本中所有后续命令。
语法:
[address1[,address2]]n示例:仅打印 books.txt 中的偶数行(使用每条目占一行的格式):
$ sed -n 'n;p' books.txt输出(假设 books.txt 至少有 6 行):
2) The Two Towers, J. R. R. Tolkien, 3524) The Fellowship of the Ring, J. R. R. Tolkien, 4326) A Game of Thrones, George R. R. Martin, 864解释:
- 第 1 行读入模式空间 (pattern space)。
-n抑制默认打印。 n: 模式空间(第 1 行)不 打印(因为有-n)。下一行(第 2 行)读入模式空间。p: 模式空间(第 2 行)打印。- 第 3 行读入模式空间。
n: 模式空间(第 3 行)不 打印。下一行(第 4 行)读入模式空间。p: 模式空间(第 4 行)打印。以此类推。
考虑一个脚本:command1; command2; n; command3; command4。command1 和 command2 操作原始行。然后 n 打印它(如果不是 -n),读取下一行,而 command3 和 command4 操作这个 新读入 的行。
与暂存空间交互
Section titled “与暂存空间交互”暂存空间 (hold space) 是 sed 的另一个缓冲区,它在读取-执行-显示循环之间持久存在。对于需要记住前一行的任务,它至关重要。sed 命令不能直接操作暂存空间;必须先将数据移动到模式空间。对于以下示例,假设 books_multiline.txt 中每本书的标题和作者分别在连续的两行中:
A Storm of SwordsGeorge R. R. MartinThe Two TowersJ. R. R. TolkienThe AlchemistPaulo CoelhoThe Fellowship of the RingJ. R. R. TolkienThe PilgrimagePaulo CoelhoA Game of ThronesGeorge R. R. Martinx 命令:交换缓冲区
Section titled “x 命令:交换缓冲区”x 命令交换模式空间和暂存空间的内容。
示例:仅打印 books_multiline.txt 中的作者姓名。
$ sed -n 'x;n;p' books_multiline.txt输出:
George R. R. MartinJ. R. R. TolkienPaulo CoelhoJ. R. R. TolkienPaulo CoelhoGeorge R. R. Martin前两行 (Title1, Author1) 的工作流程:
- 第 1 行 (Title1) 读入模式空间 (PS)。PS={Title1},HS={初始为空或包含前一个循环的内容}。
x: 交换。PS={HS 内容},HS={Title1}。n: (由于 -n 不打印)。读取第 2 行 (Author1) 到 PS。PS={Author1},HS={Title1}。p: 打印 PS,即 Author1。- 重复此过程。对于下一对,HS 最初将包含 Author1,
x后则包含 Title2。
h 和 H 命令:复制到暂存空间
Section titled “h 和 H 命令:复制到暂存空间”h: 将模式空间的内容复制到暂存空间,覆盖暂存空间的原有内容。
H: 将模式空间的内容附加到暂存空间(附加前会先添加一个换行符)。
语法:[地址1[,地址2]]h 和 [地址1[,地址2]]H
示例:仅打印作者为 “Paulo Coelho” 的书名。
$ sed -n '/Paulo Coelho/!h; /Paulo Coelho/{x;p;}' books_multiline.txt输出:
The AlchemistThe Pilgrimage解释:
- 读取书名行(例如,“The Alchemist”)时,它 不 匹配
/Paulo Coelho/。因此,h将其复制到暂存空间 (hold space)。HS= {“The Alchemist”}。 - 读取对应的作者行(“Paulo Coelho”)时,它匹配
/Paulo Coelho/。跳过!h。 - 执行块
{x;p;}:x交换模式空间 (“Paulo Coelho”) 和暂存空间 (“The Alchemist”)。现在 PS= {“The Alchemist”},HS= {“Paulo Coelho”}。 p打印模式空间内容,即书名 “The Alchemist”。
示例:打印作者为 “Paulo Coelho” 的书名及其作者。
$ sed -n '/Paulo Coelho/!h; /Paulo Coelho/{H;x;p;}' books_multiline.txt输出:
The AlchemistPaulo CoelhoThe PilgrimagePaulo Coelho解释(针对 “The Alchemist” 和 “Paulo Coelho”):
- “The Alchemist” (书名):
h将其复制到 HS。HS= {“The Alchemist”}。 - “Paulo Coelho” (作者):匹配。
H将 PS 附加到 HS。HS= {“The Alchemist\nPaulo Coelho”}。 x交换。PS= {“The Alchemist\nPaulo Coelho”},HS= {“Paulo Coelho”}。p打印 PS。
g 和 G 命令:从暂存空间检索
Section titled “g 和 G 命令:从暂存空间检索”g: 将暂存空间的内容复制到模式空间,覆盖模式空间的原有内容。
G: 将暂存空间的内容附加到模式空间(附加前会先添加一个换行符)。
语法:[地址1[,地址2]]g 和 [地址1[,地址2]]G
示例:打印作者 “Paulo Coelho”,然后打印对应的书名。
$ sed -n '/Paulo Coelho/!h; /Paulo Coelho/{p;g;p;}' books_multiline.txt输出:
Paulo CoelhoThe AlchemistPaulo CoelhoThe Pilgrimage解释(针对 “The Alchemist” 和 “Paulo Coelho”):
- “The Alchemist” (书名):
h将其复制到 HS。HS= {“The Alchemist”}。 - “Paulo Coelho” (作者):匹配。
p打印 PS (“Paulo Coelho”)。 g将 HS 复制到 PS。PS= {“The Alchemist”}。p打印 PS (“The Alchemist”)。
示例使用 G:打印作者 “Paulo Coelho”,然后打印对应的书名(结果相同)。
$ sed -n '/Paulo Coelho/!h; /Paulo Coelho/{G;p;}' books_multiline.txt解释(针对 “The Alchemist” 和 “Paulo Coelho”):
- “The Alchemist” (书名):
h将其复制到 HS。HS= {“The Alchemist”}。 - “Paulo Coelho” (作者):匹配。
G将 HS 附加到 PS。PS= {“Paulo Coelho\nThe Alchemist”}。 p打印 PS。
这些命令是进行复杂文本操作的基础构建块,例如反转段落中的行序、处理由特定模式界定的文本块,或从多行收集信息以生成摘要。例如,可以收集所有以 ‘Error:’ 开头的行,将它们存储在暂存空间中,然后在文件处理结束时一次性打印出来。
常见陷阱与深入学习:
Section titled “常见陷阱与深入学习:”- 忘记
-n: 如果你手动使用p或P控制打印,请记住使用-n来抑制自动打印,否则你会得到重复的行。 H和G命令中的换行符: 这些命令在附加前会插入一个换行符。这通常很有用,但在后续处理组合行时需要考虑。- 复杂性: 尽管功能强大,但涉及模式空间/暂存空间的复杂嵌套逻辑可能会变得难以调试。对于非常复杂的转换,
awk或像 Python、Perl 这样的脚本语言可能更清晰。 - 请查阅官方的 GNU sed 手册以获取每个命令的权威详细信息和更多示例。