正则表达式是非常强大的功能,有了正则表达式,很多很复杂的问题就迎刃而解了。
比如我们从 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'
运行结果如下
一二
一