Bash 零基础教程

sed 文本流编辑器

sed(Stream EDitor,流编辑器)是一个用于文本转换的强大命令行工具。它在文本流上运行,根据正则表达式和特定命令执行替换、删除、插入和其他操作。与许多文本编辑器不同,sed 不需要打开文件进行交互式会话。它逐行处理输入流,按要求进行修改,然后输出结果。这使其成为自动化重复性编辑任务的理想选择,尤其是在 Shell 脚本中。了解 sed 对于高效的日志文件分析、数据处理和系统管理自动化非常重要。

1. sed 的基本语法与操作

sed 的基本语法如下:

sed '命令' 输入文件

其中,命令 (command) 指定要执行的操作,输入文件 (inputfile) 是要处理的文件。如果没有指定输入文件,sed 将从标准输入读取。默认情况下,输出会被发送到标准输出,这意味着它会打印在终端屏幕上。

1.1 替换 (s) 命令

替换命令 s (Substitution) 是最常用的 sed 命令。它用替换字符串替换匹配到的模式。一般格式为:

sed 's/模式/替换内容/' 输入文件

解析:

  • s: 替换命令。
  • 模式 (pattern): 要搜索的正则表达式。
  • 替换内容 (replacement): 用于替换匹配模式的字符串。
  • /: 分隔符,可以是任何字符,但通常使用正斜杠。

示例 1:基本替换
假设你有一个名为 myfile.txt 的文件,内容如下:

This is a test file.
This file is for testing purposes.

要将 "test" 替换为 "sample",你可以使用以下命令:

sed 's/test/sample/' myfile.txt

输出:

This is a sample file.
This file is for sampling purposes.

注意:每行只有第一个出现的 "test" 被替换了。

示例 2:全局替换
要替换每行中所有出现的 "test",请使用 g (global) 标志:

sed 's/test/sample/g' myfile.txt

示例 3:使用不同的分隔符
如果你的模式或替换字符串中包含正斜杠,你可以使用不同的分隔符来避免转义它们。例如,使用 # 作为分隔符:

sed 's#this/that#that/this#g' myfile.txt

如果 myfile.txt 包含带有 "this/that" 的行,它们将被替换为 "that/this"。

1.2 删除 (d) 命令

d (Deletion) 命令删除匹配特定模式或在指定范围内的行。

示例 1:删除包含某模式的行
要从 myfile.txt 中删除所有包含单词 "testing" 的行:

sed '/testing/d' myfile.txt

示例 2:删除指定行号
要删除 myfile.txt 的第二行:

sed '2d' myfile.txt

示例 3:删除连续多行
要删除第 2 到第 4 行:

sed '2,4d' myfile.txt

1.3 插入 (i) 与追加 (a) 命令

i (Insertion) 命令在匹配模式或行号的行之前插入文本。a (Append) 命令在匹配模式或行号的行之后追加文本。

示例 1:在某行前插入文本
要在 myfile.txt 中包含单词 "testing" 的任何行之前插入一行 "New line before testing":

sed '/testing/i New line before testing' myfile.txt

示例 2:在某行后追加文本
要在 myfile.txt 中包含单词 "testing" 的任何行之后追加一行 "New line after testing":

sed '/testing/a New line after testing' myfile.txt

示例 3:在指定行号前后插入/追加
要在第 2 行前插入 "New line before line 2",并在第 2 行后追加 "New line after line 2":

sed '2i New line before line 2' myfile.txt | sed '2a New line after line 2'

1.4 更改 (c) 命令

c (Change) 命令用新文本替换整行内容。

示例 1:更改包含某模式的行
要将包含 "test" 的任何行替换为 "This line has been changed":

sed '/test/c This line has been changed' myfile.txt

示例 2:更改指定行号
要将第三行替换为 "This is the new third line":

sed '3c This is the new third line' myfile.txt

1.5 读取 (r) 与 写入 (w) 命令

  • r (Read) 命令读取另一个文件的内容,并将其追加到匹配模式或行号的行之后。 示例: 要在 myfile.txt 中包含 "test" 的任何行之后追加 otherfile.txt 的内容:
sed '/test/r otherfile.txt' myfile.txt
  • w (Write) 命令将匹配模式的行写入一个新文件。
    示例: 要将 myfile.txt 中所有包含 "test" 的行写入名为 test_lines.txt 的新文件中:
sed '/test/w test_lines.txt' myfile.txt

2. sed 中的正则表达式

sed 严重依赖正则表达式进行模式匹配。扎实理解正则表达式是有效使用 sed 的关键。(这建立在上一章对正则表达式的介绍之上)。

2.1 基础正则表达式元字符

元字符描述示例
.匹配除换行符以外的任意单个字符。a.c 匹配 "abc", "adc", "aec" 等。
*匹配前面的字符零次或多次。ab*c 匹配 "ac", "abc", "abbc" 等。
+(ERE特有) 匹配前面的字符一次或多次。ab+c 匹配 "abc", "abbc",但不匹配 "ac"。
?(ERE特有) 匹配前面的字符零次或一次。ab?c 匹配 "ac", "abc"。
[]匹配方括号内的任意单个字符。也支持范围如 [a-z][aeiou] 匹配任何元音字母。
[^]匹配不在方括号内的任意单个字符。[^0-9] 匹配任何非数字字符。
^匹配一行的开头。^abc 匹配以 "abc" 开头的行。
$匹配一行的结尾。abc$ 匹配以 "abc" 结尾的行。
\转义特殊字符,使其按字面意义匹配。a\.b 匹配 "a.b"。
()(ERE特有) 分组正则表达式的部分。可以被反向引用。(ab)+ 匹配一次或多次 "ab"。
|作为 "OR"(或)运算符。abc|def 匹配 "abc" 或 "def"。(在 BRE 中需转义,ERE 中不需要)。

示例 1:匹配特定模式
要查找 myfile.txt 中以数字开头的行:

sed -n '/^[0-9]/p' myfile.txt

-n 选项抑制默认输出,而 p 命令只打印匹配的行。^ 匹配行首,[0-9] 匹配任意数字。

2.2 扩展正则表达式 (ERE)

默认情况下,sed 使用基础正则表达式 (BRE)。要使用功能更丰富、语法更易读的扩展正则表达式 (ERE),请使用 -E 选项(在某些系统上是 -r)。ERE 允许你直接使用 +?| 和括号 () 等元字符,而无需对它们进行转义。

示例:使用 | (OR) 与 ERE
要将 "abc" 或 "def" 替换为 "xyz":

sed -E 's/abc|def/xyz/g' myfile.txt

如果没有 -E,你需要转义 |

sed 's/abc\|def/xyz/g' myfile.txt

2.3 反向引用 (Backreferences)

反向引用允许你在替换字符串中引用先前匹配的分组。它们由 \1, \2, \3 等表示,其中 \1 指代第一个捕获的分组,\2 指代第二个,依此类推。

示例:交换两个单词的位置
假设你有像 "word1 word2" 这样的行,你想交换这两个单词的顺序。

sed -E 's/([a-zA-Z]+) ([a-zA-Z]+)/\2 \1/g' myfile.txt

解析:

  • ([a-zA-Z]+): 匹配一个或多个字母并将其捕获为组 1。
  • : 匹配中间的空格。
  • ([a-zA-Z]+): 匹配一个或多个字母并将其捕获为组 2。
  • \2 \1: 将匹配到的文本替换为组 2、一个空格、然后是组 1。

3. sed 高级技巧

3.1 寻址 (Addresses) 机制

地址指定 sed 命令应该对哪些行执行操作。我们已经看到了使用行号和正则表达式模式作为地址的例子。地址可以被组合并以更复杂的方式使用。

示例:使用行号范围和模式
仅对第 5 到第 10 行应用替换命令:

sed '5,10s/old/new/g' myfile.txt

对从匹配 "start_pattern" 的行开始,到匹配 "end_pattern" 的行结束的区块应用替换:

sed '/start_pattern/,/end_pattern/s/old/new/g' myfile.txt

3.2 执行多个命令

你可以使用几种方法执行多个 sed 命令:

  • 使用 -e 选项:
sed -e 's/pattern1/replacement1/g' -e 's/pattern2/replacement2/g' myfile.txt
  • 使用分号 ;
sed 's/pattern1/replacement1/g; s/pattern2/replacement2/g' myfile.txt
  • 使用脚本文件:
    创建一个文件(例如 my_sed_script.sed),内容如下:
s/pattern1/replacement1/g
s/pattern2/replacement2/g

然后运行:

sed -f my_sed_script.sed myfile.txt

脚本文件方法对于复杂的 sed 命令序列非常有用。

3.3 保持空间 (The Hold Space)

保持空间是一个临时缓冲区,你可以在其中存储行以供以后使用。sed 提供了操作保持空间的命令:

  • h: 将模式空间(当前行)的内容复制到保持空间(覆盖)。
  • H: 将模式空间的内容追加到保持空间。
  • g: 将保持空间的内容复制到模式空间。
  • G: 将保持空间的内容追加到模式空间。
  • x: 交换模式空间和保持空间的内容。

这些命令非常强大,但一开始可能会让人感到困惑。

示例:复制一行
要复制文件中的每一行:

sed 'H;G' myfile.txt

解析:

  • H: 将当前行(模式空间)追加到保持空间。
  • G: 将保持空间(现在包含原始行)追加到模式空间。模式空间现在包含原始行以及紧跟其后的原始行(完成复制)。