准备搜索...
分类筛选
全部分类

输入关键词开始搜索

30、sed 正则表达式

正则表达式是非常强大的功能,有了正则表达式,很多很复杂的问题就迎刃而解了。

比如我们从 price: 3.14 $ 中查找价格,没有正则表达式, 我们不得不遍历所有字符,判断是否是数字和小数点。有了正则表达式,我们只需要使用 \d+.?\d* 就可以了。

sed之所以那么早出名,也得益于它早早的就支持正则表达式了。

sed的正则表达式非常强大,尤其是 GNU SED 这个版本,除了能够使用标准正则表达式,还支持 POSIX 类等正则表达式扩展。

今天,我们开始学习 sed 中的正则表达式。

标准正则表达式元字符

匹配行首 (^)

插入符号( ^ ) 是标准正则表达式中的一个元字符,表示从一行的开头开始匹配。

例如^小明 只会匹配那些以 小明 开始的行。

范例

我们现在当前目录下新建一个文件 student.txt,内容如下

小明,23岁,北京大学
小红,22岁,清华大学
小李,25岁,斯坦福大学
小王,22岁,清华大学

然后我们再测试 ^小明 是否只会匹配那些以 小明 开始的行。

[www.ddkk.com]$ sed -n '/^小明/ p' student.txt

运行结果如下

小明,23岁,北京大学

匹配行尾 ( $ )

美元符号( $ ) 是表示从一行必须以 $ 符号前的文本结束。

例如清华大学 $ 只会匹配那些以 清华大学 结束的行。

范例

我们现在当前目录下新建一个文件 student.txt,内容如下

小明,23岁,北京大学
小红,22岁,清华大学
小李,25岁,斯坦福大学
小王,22岁,清华大学

然后我们再测试 清华大学 $ 只会匹配那些以 清华大学 结束的行。

[www.ddkk.com]$ sed -n '/清华大学$/ p' student.txt

运行结果如下

小红,22岁,清华大学
小王,22岁,清华大学

匹配任意单个字符(.)

点号(. ) 是匹配任意单个非换行符的字符。也就是说是什么无所谓,但数量只能是 1 个。

例如.at 匹配 bat、cat、mat 等等,但是不能匹配 at 或 ccat 等

[www.ddkk.com]$ echo -e "cat\nbat\nrat\nmat\nbatting\nrats\nmats" | sed -n '/^.at$/p'

运行结果如下

cat
bat
rat
mat

匹配任意存在字符列表中的一个字符( [] )

标准的正则表达式使用 方括号 [] 表示字符列表,俗称字符集。

字符列表 [] 用于匹配任意 单 个 存在 [] 里的字符。

例如[CT] 可以匹配单个 C 或 T ,但是不能匹配其它字符,也不能匹配 CT。

[www.ddkk.com]$ echo -e "Call\nTall\nBall" | sed -n '/[CT]all/ p'

运行结果如下

Call
Tall

匹配任意不在字符列表中的一个字符 ( [^] )

标准的正则表达式使用 方括号 [] 表示字符列表,俗称字符集。

字符列表 [^] 用于匹配任意 单 个 不存在 [^] 里的字符。

^ 本身除外,如果不需要匹配 ^ 则需要重复输入 ^。

例如[^CT] 可以任意单个字符,除了 C 或 T。

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

运行结果如下

Ball

匹配连续范围的字符 ( [-] )

所谓 连续范围的字符,就是按照 ASCII 表中出现的先后顺序排列的字符列表。

标准的正则表达式使用 破号 也就是 中划线( - ) 来表示连续范围的字符。

-左边的字符表示开始,- 左边的字符表示结束。

例如0-9 表示 0123456789,例如 a-e 表示 abcde。

标准的正则表达式使用 方括号 [] 表示字符列表,俗称字符集。

因此[-] 用于匹配任意 单 个 存在 连续范围的字符集 [] 里的字符。

例如[C-Z] 表示任意单个存在字符列表 [CDEFGHIJKLMNOPQRSTUVWXYZ] 里的字符。

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

运行结果如下

Call 
Tall

如果我们将连续字符集改为 A-P

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

运行结果如下

Call 
Ball

只出现 0 次或 1 次 ( ? )

标准的正则表达式使用 问号(?) 表示前面的字符只出现 0 次或 1 次。

但是,因为 sed 将 ? 当作了普通字符,所以使用 ? 来表示前面的字符只出现 0 次或 1 次。

苹果电脑自带的 sed 不支持 ?。

例如Pea?r 可以匹配 Per 或 Pear,但不能匹配 Peaar。

[www.ddkk.com]$ echo -e "Pear\nPeaar\nPer" | sed -n '/Pea\?r/ p'

运行结果如下

Pear
Per

至少出现一次 ( + )

标准的正则表达式使用 加号(+) 表示前面的字符需要出现至少一次。

因为sed 将 + 当作了普通字符,所以使用 + 来表示前面的字符需要出现至少一次。

苹果电脑自带的 sed 不支持 +。

例如Pea+r 可以匹配 Pear 或 Peaar,但不能匹配 Per。

[www.ddkk.com]$ echo -e "Pear\nPeaar\nPer" | sed -n '/Pea\+r/ p'

运行结果如下

Pear
Peaar

出现任意次数 (*)

标准的正则表达式使用 星号(*) 表示前面的字符可以出现任意次数。也就是可以出现 0 次或 1 次或更多次数。

例如ca*t 可以匹配 ct、cat 或 caat 等等。

[www.ddkk.com]$ echo -e "ct\nca\ncat\ncaat" | sed -n '/ca*t/ p'

运行结果如下

ct
cat
caat

精确出现 n 次 ( {n} )

标准的正则表达式使用 {n} 表示前面的字符需要精确出现 n 次。

因为sed 将 {n} 当作了普通字符,所以使用 {n} 来表示前面的字符需要精确出现 n 次。

例如^8{3} $ 只能匹配 888 而不能匹配 88 或 8888。

[www.ddkk.com]$ echo -e "8\n88\n888\n8888" | sed -n '/^8\{3\}$/ p'

运行结果如下

888

至少出现 n 次 ( {n,} )

标准的正则表达式使用 {n,} 表示前面的字符需要至少出现 n 次。

因为sed 将 {} 当作了普通字符,所以使用 {n,} 来表示前面的字符需要至少出现 n 次。

例如^8{3,} $ 可以匹配 888 或 8888 但不能匹配 88。

[www.ddkk.com]$ echo -e "8\n88\n888\n8888" | sed -n '/^8\{3,\}$/ p'

运行结果如下

888
8888

出现 M 到 N 次

{m, n} 用于表示前面字符的至少出现 m 次,最多出现 n 次。

因为sed 将 {} 当作了普通字符,所以使用 {m,n} 来表示前面字符的至少出现 m 次,最多出现 n 次。

例如^8{2,4} $ 可以匹配 88、888、8888 但是不会匹配 8 和 88888。

[www.ddkk.com]$ echo -e "8\n88\n888\n8888\n88888" | sed -n '/^8\{3,\}$/ p'

运行结果如下

888
8888
88888

管道符 (|)

竖线| 又称 管道符,在所有的正则表达式中都有着特殊的意义。它用于表示 或 的意思。

管道符通常和 小括号 () 一起使用,用于从两个选择中匹配一个即可。

苹果电脑自带的 sed 不支持管道符 (|)。

例如正则表达式 /str(1|3)/ 既可以匹配 str1 也可以匹配 str3。

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

运行结果如下

str1 
str3

注意: 管道符和小括号需要使用 \ 转义。

正则表达式转义字符

正则表达式中还有一类特殊意义的字符,它们通常由 正斜杠(\) 和小写字母组合而成。

例如,新行使用 \n 表示,回车使用 \r 表示等等等等。我们称这些字符为 转义字符。

sed的正则表达式匹配这些特殊意义的转义字符需要再转义一次,也就是在前面在添加一个 正斜杠(\) 。

例如\n 改成 \n,\r 改成 \r。

本小节,我们开始学习 sed 中支持的那些转义字符。

正斜杠 "\"

因为 正斜杠 "\" 和其它任意字母组合在一起都有一个特殊意思,就是转义这个字母。

因此如果要匹配 正斜杠 "\" ,则需要在前面额外添加一个 正斜杠 "\" 。

[www.ddkk.com]$ echo 'str1\str2' | sed -n '/\\/ p'

运行结果如下

str1\str2

换行符 "\n"

\n 用于表示 换行符。

如果要匹配两个字母 \n ,那么在使用的时候需要额外添加一个 正斜杠(\) 。

[www.ddkk.com]$ echo 'str1\nstr2' | sed -n '/\\n/ p'

运行结果如下

str1\nstr2

回车符 "\r"

\r 用于表示 回车符。

如果要匹配两个字母 \r ,那么在使用的时候需要额外添加一个 正斜杠(\) 。

[www.ddkk.com]$ echo 'str1\rstr2' | sed -n '/\\r/ p'

运行结果如下

str1\rstr2

十进制字符 "\dnnn"

\dnnn 用于表示十进制字符 nnn。

苹果电脑自带的 sed 不支持 \dnnn。

例如\d97 用于表示字母 a,查 ASCII 表可得数字 97 表示字母 a。

[www.ddkk.com]$ echo -e "a\nb\nc" | sed -n '/\d97/ p'

运行结果如下

a

八进制 "\onnn"

\onnn 用于表示八进制字符 nnn。

o是字母 opq 中的 o 而不是数字 0。

苹果电脑自带的 sed 不支持 \onnn。

例如\o142 用于表示字符 b。 \o142 转换为十进制就是 98,查 ASCII 表可得字母 b。

[www.ddkk.com]$ echo -e "a\nb\nc" | sed -n '/\o142/ p'

运行结果如下

b

十六进制 "\xnnn"

\xnnn 用于表示十六进制字符 nnn。

苹果电脑自带的 sed 不支持 \xnnn。

例如\x64 用于表示字符 c。 \o63 转换为十进制就是 99,查 ASCII 表可得字母 c。

[www.ddkk.com]$ echo -e "a\nb\nc" | sed -n '/\x63/ p'

运行结果如下

c

POSIX 正则表达式

sed支持 POSIX 规范下的正则表达式。

POSIX 正则表达式有个特点,就是支持一些具有特殊含义的 保留词。

这些特殊的 保留词 在 POSIX 中被称为 POSIX 类。

接下来的一小节,我们就来了解下 sed 支持的 POSIX 类。

字母数字 [:alnum:]

[:alnum:] 用于表示任意单个大小写字母 a-zA-Z 或单个数字 0-9。

用上面所学的知识,[[:alnum:]] 其实就是 [0-9a-zA-Z]。

例如[[:alnum:]] 可以匹配 One 或 123 但是不能比配 \t。

[www.ddkk.com]$ echo -e "One\n123\n\t" | sed -n '/[[:alnum:]]/ p'

运行结果如下

One 
123

字母 [:alpha:]

[:alpha:] 用于表示任意单个大小写字母 a-zA-Z。

用上面所学的知识,[[:alpha:]] 其实就是 [a-zA-Z]。

例如[[:alpha:]] 可以匹配 One 但是不能匹配 123 或 \t。

[www.ddkk.com]$ echo -e "One\n123\n\t" | sed -n '/[[:alpha:]]/ p'

运行结果如下

One

空白符 [:blank:]

[:blank:] 用于表示任意单个空格 ' ' 或制表符 \t。

例如[:blank:] 可以匹配 \t 但是不会匹配 One 或 123。

[www.ddkk.com]$ echo -e "One\n123\n\t" | sed -n '/[[:space:]]/ p' | cat -vte

运行结果如下

^I$

注意, 命令 cat -vte 会将制表符 \t 显示为 ^I

数字 [:digit:]

[:digit:] 用于表示任意单个数字 0-9。

用上面所学的知识,[[:digit:]] 其实就是 [0-9]。

例如[[:digit:]] 可以匹配 123 但是不能匹配 One 或 \t。

[www.ddkk.com]$ echo -e "abc\n123\n\t" | sed -n '/[[:digit:]]/ p'

运行结果如下

123

小写字母 [:lower:]

[:lower:] 用于表示任意单个小写字母 a-z。

用上面所学的知识,[[:lower:]] 其实就是 [a-z]。

例如[[:lower:]] 可以匹配 one 但是不能匹配 TWO 或 \t。

[www.ddkk.com]$ echo -e "one\nTWO\n\t" | sed -n '/[[:lower:]]/ p'

运行结果如下

one

大写字母 [:upper:]

[:upper:] 用于表示任意单个大写字母 A-Z。

用上面所学的知识,[[:upper:]] 其实就是 [A-Z]。

例如[[:upper:]] 可以匹配 TWO 但是不能匹配 one 或 \t。

[www.ddkk.com]$ echo -e "one\nTWO\n\t" | sed -n '/[[:upper:]]/ p'

运行结果如下

TWO

标点符号 [:punct:]

[:punct:] 用于表示任意单个标点符号。

例如[:punct:] 可以匹配 One,123 但是不匹配 Three 和 123

[www.ddkk.com]$ echo -e "One,Two\nThree\n123" | sed -n '/[[:punct:]]/ p'

运行结果如下

One,Two

空白符 [:space:]

[:space:] 用于表示任意单个空白符。

ASCII 编码中,空白符包含 空格(' ') 、制表符('\t') 、回车符('\r') 、换行符('\n') 、垂直制表符('\v') 和 换页符('\F')

例如[:blank:] 可以匹配 \t 和 \f 但是不会匹配 One。

[www.ddkk.com]$ echo -e "One\n123\f\n456\t" | sed -n '/[[:space:]]/ p' | cat -vte

输出结果如下

123^L$
456^I$

正则表达式元字符

跟其它语言的正则表达式一样,SED 同样支持正则表达式元字符。

元字符 通常由 正斜杠(\) 和 一个字母组合而成。它们是一体的,放在一起表示单个字符而不是两个字符。 这也是元字符的由来。

sed所支持的正则表达式元字符,是从 Perl 借鉴过来的。

需要注意,元字符几乎只有 GNU SED 支持,其它的 SED 变体可能就不支持了。比如苹果电脑自带的 sed 就不支持。

下面,我们开始简单的介绍下几个会经常用到的元字符。

单词边界 \b

在正则表达式中,\b 并不表示 回退,而是表示 单词边界。

苹果电脑自带的 sed 不支持 \b。

那什么是单词边界呢?

单词边界就是一个单词和另一个单词的区分标志,通常是 空格 或 换行 等。

例如/\bthe\b/ 可以匹配 the 但是不能匹配 these 或 there, they, then 等等。

[www.ddkk.com]$ echo -e "these\nthe\nthey\nthen" | sed -n '/\bthe\b/ p'

运行结果如下

the

非单词边界 \B

\B 和 \b 的作用正好相反,用于匹配非单词边界。

那什么是单词边界呢?

单词边界就是一个单词和另一个单词的区分标志,通常是 空格 或 换行 等。

例如/the\B/ 可以匹配 these 和 they 但是不能匹配 the。

[www.ddkk.com]$ echo -e "these\nthe\nthey" | sed -n '/the\B/ p'

运行结果如下

these
they

单个空白符 \s

\s 用于匹配单个空白符。

ASCII 编码中,空白字符包含 空格(' ') 、制表符('\t') 、回车符('\r') 、换行符('\n') 、垂直制表符('\v') 和 换页符('\F')

苹果电脑自带的 sed 不支持 \s。

例如/Line\s/ 可以匹配 Line\t,但是不会匹配 Line2。

[www.ddkk.com]$ echo -e "Line\t\nLine2" | sed -n '/Line\s/ p'

运行结果如下

Line

单个非空格 \S

\S 用于匹配单个非空白符。

ASCII 编码中,空白字符包含 空格(' ') 、制表符('\t') 、回车符('\r') 、换行符('\n') 、垂直制表符('\v') 和 换页符('\F')

苹果电脑自带的 sed 不支持 \S。

例如/Line\S/ 可以匹配 Line2 但是不会匹配 Line\t 。

[www.ddkk.com]$ echo -e "Line\t1\nLine2" | sed -n '/Line\S/ p'

运行结果如下

Line2

单个字符 \w

\w 用于匹配单个字符。

并不是所有的字符都可以被匹配的,ASCII 编码中只有 数字 0-9、大小写字母 和 下划线 三种类型的字符才能被 \w 匹配到。

苹果电脑自带的 sed 不支持 \w。

例如下面的命令

[www.ddkk.com]$ echo -e "一\nOne\n123\n1_2\n&;#" | sed -n '/\w/ p'

运行结果如下

一
One
123
1_2

单个非字符 \W

从上面的学习中,我们知道 \w 用于匹配单个字符:数字 0-9、大小写字母 和 下划线。

而\W 则是匹配 \w 之外的单个字符,也就是不包含在 数字 0-9、大小写字母 和 下划线 中的其它 ASCII 单个字符。

苹果电脑自带的 sed 不支持 \W。

例如下面的命令

[www.ddkk.com]$ echo -e "一\nOne\n123\n1_2\n&;#" | sed -n '/\W/ p'

运行结果如下

&;#

模式空间开始 ( '' )

sed提供了特殊字符 反引号(\)用于表示 模式空间 的开始。

\ 是英文输入法状态下数字键 1 左边的那个键。

苹果电脑自带的 sed 不支持该特殊字符。

例如下面的 sed 命令

[www.ddkk.com]$ echo -e "一二\n三\n一" | sed -n '/\一/ p'

运行结果如下

一二
一