Skip to content

Sed 正则表达式

正则表达式(通常缩写为 regex)是 Sed 强大和高效的基石,使它能够执行复杂的文本操作。对于任何命令行用户来说,对正则表达式有扎实的理解是极有价值的。

与许多 GNU/Linux 工具一样,Sed 支持正则表达式。本章将深入探讨 Sed 中使用的 regex 语法的复杂性。我们将涵盖 Sed 默认使用的基本正则表达式(Basic Regular Expressions,BRE),POSIX 字符类以及特殊的 GNU Sed 元字符。我们还将稍微提及扩展正则表达式(Extended Regular Expressions,ERE),它可以简化许多模式。

理解基本正则表达式与扩展正则表达式 (BRE vs. ERE)

Section titled “理解基本正则表达式与扩展正则表达式 (BRE vs. ERE)”

Sed 默认使用基本正则表达式(BRE)。在 BRE 中,某些字符如 ?、+、{}、|、( 和 ) 失去了其特殊含义,必须用反斜杠转义(例如,\?、\+、\{\}、\|、\(\))。

大多数现代 Sed 版本(特别是 GNU Sed)通过命令行选项 -E 或 -r(对于 GNU Sed)支持扩展正则表达式(ERE)。在 ERE 中,这些字符无需反斜杠即可重新获得其特殊含义,从而使模式更清晰、更易读。本教程将主要使用 BRE 语法,因为它作为 Sed 的默认模式,但我们经常会指出 ERE 如何简化表达式。

脱字符号 (^) 匹配模式空间的开头(通常是文本行)。以下示例打印 books.txt 中所有以单词 “The” 开头的行。

[user]$ sed -n '/^The/p' books.txt

假设 books.txt 包含:

  1. A Storm of Swords, J. R. R. Tolkien
  2. The Two Towers, J. R. R. Tolkien
  3. The Alchemist, Paulo Coelho
  4. The Fellowship of the Ring, J. R. R. Tolkien
  5. The Pilgrimage, Paulo Coelho

执行该命令得到:

2) The Two Towers, J. R. R. Tolkien
3) The Alchemist, Paulo Coelho
4) The Fellowship of the Ring, J. R. R. Tolkien
5) The Pilgrimage, Paulo Coelho

美元符号 ($) 匹配模式空间的末尾。以下示例打印以 “Coelho” 结尾的行。

[user]$ sed -n '/Coelho$/p' books.txt

执行该命令得到:

3) The Alchemist, Paulo Coelho
5) The Pilgrimage, Paulo Coelho

点号 (.) 匹配除换行符外的任何单个字符。以下示例从输入流中打印以 ‘t’ 结尾的三个字母的单词。

[user]$ echo -e "cat\nbat\nrat\nmat\nbatting\nrats\nmats" | sed -n '/^..t$/p'

执行该命令得到:

cat
bat
rat
mat

方括号 [] 定义一个字符集,匹配集合中的任何单个字符。例如,/[CT]all/ 匹配 “Call” 或 “Tall”。

[user]$ echo -e "Call\nTall\nBall" | sed -n '/[CT]all/p'

执行该命令得到:

Call
Tall

方括号内的第一个字符如果是脱字符 ^,则表示对集合取反,匹配 不 在该集合中的任何单个字符。/[^CT]all/ 匹配 “Ball”,但不匹配 “Call” 或 “Tall”。

[user]$ echo -e "Call\nTall\nBall" | sed -n '/[^CT]all/p'

执行该命令得到:

Ball

字符集内的连字符 - 指定一个范围。/[C-Z]all/ 匹配从 C 到 Z 的任何大写字母后跟 “all”。

[user]$ echo -e "Call\nTall\nBall" | sed -n '/[C-Z]all/p'

执行该命令得到:

Call
Tall

修改范围为 /[A-P]all/ 会改变匹配结果:

[user]$ echo -e "Call\nTall\nBall" | sed -n '/[A-P]all/p'

执行该命令得到:

Call
Ball

量词指定前面的字符或组可以出现的次数。记住在 BRE 中需要转义这些字符。

转义的问号 \? 匹配前面的字符零次或一次出现。(在 ERE 中,使用 ?)。这使得该字符成为可选的。示例匹配 “Behaviour” 和 “Behavior”。

[user]$ echo -e "Behaviour\nBehavior" | sed -n '/Behaviou\?r/p'

执行该命令得到:

Behaviour
Behavior

转义的加号 \+ 匹配前面的字符一次或多次出现。(在 ERE 中,使用 +)。示例匹配包含一个或多个 ‘2’ 的行。

[user]$ echo -e "111\n22\n123\n234\n456\n222" | sed -n '/2\+/p'

执行该命令得到:

22
123
234
222

星号 * 匹配前面的字符零次或多次出现。(在 BRE 中用于此基本用法时不需要转义)。示例匹配 “ca”、“cat”、“catt” 等。

[user]$ echo -e "ca\ncat\ncaz" | sed -n '/cat*/p'

执行该命令得到:

ca
cat

转义的花括号 \{N\} 恰好匹配前面的字符 N 次出现。(在 ERE 中,使用 {N})。示例只打印 numbers.txt 中的三位数字。

[user]$ cat numbers.txt
1
10
100
1000
10000
[user]$ sed -n '/^[0-9]\{3\}$/p' numbers.txt

执行该命令得到:

100

\{N,\} 匹配至少 N 次出现。(在 ERE 中,使用 {N,})。示例打印五位或更多位数字的数字。

[user]$ sed -n '/^[0-9]\{5,\}$/p' numbers.txt

执行该命令得到:

10000

\{N,M\} 匹配至少 N 次,最多 M 次出现。(在 ERE 中,使用 {N,M})。示例打印至少三位但不超过四位数字的数字。

[user]$ sed -n '/^[0-9]\{3,4\}$/p' numbers.txt

执行该命令得到:

100
1000

转义的管道符 \|(在转义的括号 \(...\) 内)充当逻辑或 (OR),匹配其左侧或右侧的表达式。(在 ERE 中,使用 | 和 (...))。示例匹配 “str1” 或 “str3”。

[user]$ echo -e "str1\nstr2\nstr3\nstr4" | sed -n '/str\(1\|3\)/p'

执行该命令得到:

str1
str3

注意:对于复杂的交替或分组,强烈推荐使用 -E(或 -r)来使用扩展正则表达式以提高可读性:sed -E -n '/str(1|3)/p'。

要匹配在 regex 中具有特殊含义的字符(如 .、*、[、$、^),您必须用反斜杠 \ 转义它们。例如,要匹配字面上的点,使用 \.)。一些序列如 \n(换行符)或 \t(制表符)表示不可打印字符,尽管它们在 Sed 模式中的解释可能很棘手,通常最好在 s 命令的替换部分使用它们或依靠 POSIX 字符类。

要匹配字面上的反斜杠,您需要对其进行转义:\\。

[user]$ echo 'path\to\file' | sed -n '/to\\file/p'

执行该命令得到:

path\to\file

转义其他特殊字符(例如,\. 表示字面上的点)

Section titled “转义其他特殊字符(例如,\. 表示字面上的点)”

要匹配字面上的点号(.),您必须将其转义为 \.)。否则,. 匹配任何字符。

[user]$ echo -e "file.txt\nfile_txt" | sed -n '/file\.txt/p'

执行该命令得到:

file.txt

虽然 Sed 主要处理文本行,但某些版本(特别是 GNU Sed)允许按其 ASCII 值匹配字符。这不太常见,且通常与平台相关。

十进制 ASCII (\dNNN - GNU Sed)

\dNNN 按其十进制 ASCII 值 NNN 匹配字符。示例:\d97 代表 ‘a’。

[user]$ echo -e "a\nb\nc" | sed -n '/\d97/p' # GNU Sed specific

执行该命令(使用 GNU Sed)得到:

a

八进制 ASCII (\oNNN - GNU Sed)

\oNNN 按其八进制 ASCII 值 NNN 匹配字符。示例:\o142 代表 ‘b’。

[user]$ echo -e "a\nb\nc" | sed -n '/\o142/p' # GNU Sed specific

执行该命令(使用 GNU Sed)得到:

b

十六进制 ASCII (\xHH - GNU Sed)

\xHH 按其十六进制 ASCII 值 HH 匹配字符。示例:\x63 代表 ‘c’。

[user]$ echo -e "a\nb\nc" | sed -n '/\x63/p' # GNU Sed specific

执行该命令(使用 GNU Sed)得到:

c

POSIX 字符类是在字符集 [] 中用 [: 和 :] 包围的特殊关键字。它们提供了一种可移植的方式来匹配常见的字符类型,同时遵守区域设置。

匹配字母和数字。示例:/[[:alnum:]]/ 匹配包含至少一个字母或数字的行。

[user]$ echo -e "One\n123\n@#\n\t" | sed -n '/[[:alnum:]]/p'

执行该命令得到:

One
123

只匹配字母。示例:/[[:alpha:]]/ 匹配包含至少一个字母的行。

[user]$ echo -e "One\n123\n@#\n\t" | sed -n '/[[:alpha:]]/p'

执行该命令得到:

One

匹配空格和制表符。示例匹配只包含一个制表符的行。cat -vte 用于可视化不可打印字符(^I 代表制表符,$ 代表行尾)。

[user]$ echo -e "Word\n \n\t" | sed -n '/^[[:blank:]]\+$/p' | cat -vte

执行该命令得到(只包含空格或制表符的行):

^I$

匹配十进制数字 (0-9)。在许多区域设置中等效于 [0-9])。示例:/[[:digit:]]/ 匹配包含至少一个数字的行。

[user]$ echo -e "abc\n123\n@#\n\t" | sed -n '/[[:digit:]]/p'

执行该命令得到:

123

匹配小写字母。示例:/[[:lower:]]/ 匹配包含至少一个小写字母的行。

[user]$ echo -e "one\nTWO\n123\n\t" | sed -n '/[[:lower:]]/p'

执行该命令得到:

one

匹配大写字母。示例:/[[:upper:]]/ 匹配包含至少一个大写字母的行。

[user]$ echo -e "one\nTWO\n123\n\t" | sed -n '/[[:upper:]]/p'

执行该命令得到:

TWO

匹配标点符号(例如,.、,、;、!、?)。示例:/[[:punct:]]/ 匹配包含至少一个标点符号的行。

[user]$ echo -e "One,Two\nThree Four\nEnd!" | sed -n '/[[:punct:]]/p'

执行该命令得到:

One,Two
End!

匹配空白字符,包括空格、制表符、换行符、回车符、换页符和垂直制表符。以下示例使用 cat -vte 可视化包含空格、换页符 (^L) 和制表符 (^I) 的行。

[user]$ echo -e "Word\nLine with space \f\t" | sed -n '/[[:space:]]/p' | cat -vte

执行该命令得到(所有输入行都匹配,因为它们包含空格或其他空白字符):

Word$
Line with space ^L^I$

GNU Sed 提供附加的元字符,通常受 Perl 启发,可以简化常见的匹配任务。这些是 GNU 扩展,在其他 Sed 版本中可能不可用。使用这些元字符时,最好意识到潜在的移植性问题,或者如果需要严格遵守 POSIX 标准,则使用 --posix 选项。

\b 匹配单词字符(即 [:alnum:] 加上 _)与非单词字符之间的零宽度边界,或者在行首/行尾(如果与单词字符相邻)。例如,\bthe\b 将 “the” 作为整个单词匹配,但不匹配 “these” 或 “other” 中的 “the”。

[user]$ echo -e "these\nthe theme\nthey\nthen" | sed -n '/\bthe\b/p'

执行该命令得到:

the theme

\B 匹配一个 不是 单词边界的零宽度位置。例如,the\B 匹配 “the” 当它是 “these” 或 “they” 等更大单词的一部分时,但不匹配作为整个单词的 “the”。

[user]$ echo -e "these\nthe\nthey" | sed -n '/the\B/p'

执行该命令得到:

these
they

\s 匹配任何空白字符(等效于 [[:space:]])。示例匹配 “Line” 后跟任何空白字符。

[user]$ echo -e "Line\t1\nLine 2\nLine3" | sed -n '/Line\s/p'

执行该命令得到:

Line\t1
Line 2

\S 匹配任何非空白字符(与 \s 相反;等效于 [^[:space:]])。示例匹配 “Line” 后跟一个非空白字符。

[user]$ echo -e "Line\t1\nLine 2\nLine3" | sed -n '/Line\S/p'

执行该命令得到:

Line3

\w 匹配任何单词字符(字母数字字符加上下划线:[[:alnum:]_])。

[user]$ echo -e "One\n123\n1_2\n&;#" | sed -n '/^\w\+$/p'

执行该命令得到的行只包含单词字符:

One
123
1_2

\W 匹配任何非单词字符(与 \w 相反;[^[:alnum:]_])。

[user]$ echo -e "One\n123\n1_2\n&;#" | sed -n '/\W/p'

执行该命令得到的行包含非单词字符:

1_2
&;#

\``(反斜杠后引号)匹配模式空间的开头。这类似于 ^,但在某些高级多行场景下与 D 等命令的行为不同。对于大多数单行处理,^` 更常用且更具可移植性。

[user]$ echo -e "One\nTwo One" | sed -n '/\`One/p'

执行该命令得到:

One

\'(反斜杠单引号)匹配模式空间的末尾。类似于 $,但在多行处理中有细微差别。对于单行处理,$ 是标准的。

[user]$ echo -e "End Note\nNote" | sed -n '/Note\'/p'

执行该命令得到:

End Note
Note

进一步学习:要获取更多关于正则表达式的深入信息,请查阅您系统上的 regex(7) man 手册页或在线资源,例如 regex101.com(选择 PCRE/Perl 风格可获得与 GNU 扩展类似的模式,或选择 POSIX ERE/BRE 获得标准 Sed 模式)。