Regular Expressions
许多 Unix commands
能够识别正则表达式,提供了一种匹配模式。比如使用正则表达式进行过滤,有些命令是不能使用正则表达式的,比如 ls
,但是可以接受通配符 *
, ?
, []
。
书中以 ed
为例演示正则表达式。
1,$s/old/new/g
1,$
从第一行到最后一行,s
表示进行替换,old
表示要替换的内容,可以是正则表达式匹配,new
是新的内容,g
表示全局替换。
匹配任意单字符 The Period .
正则表达式中的 .
号匹配 any single character,无论是什么都能匹配。
比如 r.
匹配 r 后跟任何单个字符。.x.
匹配由任意两个字符包围的 x,不一定需要相同。
匹配开头 插入符 The Caret ^
如果插入符是在一个正则表达式的开头,会匹配行的开头,^George
。
- 在每一行插入
>>
符号,1,$s/^/>>/
- 找到以
the
开头的行,/^the/
匹配结尾 $
匹配结尾,contents$
匹配以 contents
结尾的行,那么 .$
匹配什么呢?是以 .
号结尾的行吗,并不是,而是会匹配任何单个字符结尾的行,包括 .
。
那么要怎样匹配 .
呢,如果想匹配任何在正则表达式中有特殊意义的字符,使用 \
backslash 覆盖特殊意义。\.$
会匹配以 .
结尾的行。^\.
匹配以 .
开头的行。
1,$s/$/>>/
为每行末尾添加 >>
。 1,$s/..$//
去除每行末尾两个字符。 ^$
匹配空行, 空行意味着没有任何字符的行,而 ^ $
则匹配只有一个空格的行,不包含其他任何字符。
使用 [] 构造器匹配字符集
[chars]
匹配这些字符中的一个,也可以用 -
指定范围。[0-9]
匹配一个数字。 [tT]he
匹配 t 和 T 之后立即跟随 he
,也就是 the
和 The
。
1,$s/[aeiouAEIOU]//g
删除所以元音字符。[0123456789]
== [0-9]
。/[0-9]/
找包含数字的行,^[A-Z]
以大写字母开头的行,1,$s/[A-Z]/*/g
把所有大写字母替换成 *
。
*
号也有特殊含义,但是在 ed
中,只有在匹配字符串中,特殊符号才有对应的意义,在替换字符串中没有特殊意义。
如果 ^
出现在 [
之后,则表示取反。[^A-Z]
匹配除大写字母外的所有字符,[^A-Za-z]
匹配任何非字母字符,1,$s/[^A-Za-z]//g
,删除所有非字母字符。
使用 * 匹配任意多个字符
shell 使用 Asterisk 来进行 filename substitution,会匹配 0 或者 多个字符。形成正则表达式时,星号用于匹配正则表达式的前一个元素零次或多次出现。
比如 X*
匹配 0、1、2… 个大写 X
,而 XX*
则匹配一个或多个 X
,因为表达式中指定了一个 X
后跟随 0 个或多个 X
,可以使用 +
号完成同样的事,+
匹配一个或多个,至少出现一次,所以 XX*
和 X+
是等价的。
1,$s/ */ /g
把所有多余的空格去掉,This is an example
—> This is an example
,这个表达式告诉 ed
把所有一个空格后跟 0 或多个空格的字符串替换成一个空格。
.*
通常用来匹配0多个任意字符。Bear in mind that a regular expression matches the longest string of characters that match the pattern. 最长匹配原则,所以这个表达式总是匹配整行。
e.*e
匹配一行中从第一个 e
到最后一个 e
的所有字符。element
在 vim
中使用 :%s/e.*e/a/gc
替换为 ant
,最长匹配。
怎么匹配 -
和 ]
呢,[-0-9]
或者 [0-9-]
, []a-z]
。
匹配精确数量的字符 \{ ...\}
XX*
匹配 X
后跟随 0 或多个 X
,XXX*
匹配至少两个 XX
后跟随 0 或多个 X
,有更方便的方式 \{min, max\}
完成这个任务,min
表示最少匹配多少次前面的正则表达式的模式,max
表示最多匹配多少次,必须使用转义字符转义。还有种特殊模式 \{10\}
表示前面的正则模式必须匹配 10 次,才算匹配上,如果只有一个数字 \{1,\}
则表示至少出现一次到最长的那次,+\{5,\}
匹配至少五个加号,如果超过五个,匹配最长的那个。
[A-Za-z]\{4,7\}
匹配长度为 4 到 7 的字母串。1,$s/^.\{10\}//
删除每行开头的 10 个字符,如果不足 10 就不会删除,如果硬要删呢,使用 1,$s/^.\{0,10\}//
。
1,$s/.\{5\}$//
删除每行最后 5 个字符。
1,$s/[a-zA-Z]\{6,\}/X/g
把长度为 6 和以上的单词替换成 X
。
保存匹配到的字符 (…)
通过将这些字符括在反斜杠括号内,可以引用与正则表达式匹配的字符。这些被捕获的字符会存在正则表达式解析器预定义的 register
中,一共九个,1 到 9。
比如 ^\(.\)
匹配行上的第一个字符并存到 register 1 中。可以使用 \n
来引用存在 register 中的内容 n >= 1 and n <= 9
。
比如 ^\(.\)\1
匹配行上的第一个字符,并把它存到 register 1 中,然后匹配存在 register 1 中的字符,最终效果是行上开头两个字符相等就匹配。
比如 ^\(.\).*\1$
匹配每行开头字符和结尾字符相等的行。
比如 ^\(...\)\(...\)\2\1
匹配 12 个字符,其中 1-3 个字符和 10-12 相等,4-6 和 7-9 相等。
:%s/\([a-z]*\) *\([a-z0-9]*\)/\2 \1/gc
交换两列。
正则表达式太神奇了。
总结
符号 | 含义 | example | 匹配 |
---|---|---|---|
. |
Any character | a.. |
a后面跟两个任意字符 |
^ |
Beginning of line | ^wood |
以 wood 开头的行 |
$ |
End of line | x$ |
最后一个字符是 x |
^INSERT$ |
只包含 INSERT 的行 |
||
^$ |
空行 | ||
* |
前面的正则出现 0 或多次 | X* XX* |
0 或多个 X 1 或 多个 x |
+ |
前面的正则出现 1 或多次 | X+ XX+ |
1 或多个 2 或多个 |
[chars] |
匹配列表中任意一个 | [tT] [a-z] |
大小写 t 小写字母中的一个 |
[^chars] |
不匹配列表中的任意一个 | [^0-9] |
非数字 |
\{min, max\} |
前面的正则至少和之多出现几次 | x\{1,5\} |
至少1个 x 或 至多 5 个 x |
\(...\) |
保存匹配到的字符到注册器上 | ^\(.\) |
第一个字符保存到 register 1 上 |
cut
paste
连接不同文件中对应的行。
paste -d "ab" file1 file2 file3
连接符号用来分隔不同部分,a
连接 file1
和 file2
,b
连接 file2
和 file3
。如果只指定一个,那么会应用到多个文件中。
最好用单引号包裹连接符。
-s
选项对单个文件操作,把单个文件中的每一行用连接符连接起来。paste -s -d '+' numbers
。
sed
sed is stream ed,a program used for editing data in a pipe or command sequence.
尽管命令和 ed
相似,但不同的是,它不能交互式使用,通用格式 sed command file
,command
是 ed
风格的,作用于文件中的每一行,如果未指定文件,则从 STD IN
读入。
sed 's/Unix/UNIX' intro
,养成使用单引号包裹命令的习惯。 把文件中每行第一个出现的 Unix
替换为大写,如果要把行中所有出现的匹配都替换掉要使用全局模式(g)。
who | sed 's/ .*$//'
提取用户名。
-n Option
默认情况下 sed
会把输入的每一行都输出到标准输出,不管行是否发生了变化。有时,只想从文件中提出某些行。
sed -n '1,2p' intro
,只打印前两行,sed -n '/UNIX/p' intro
,打印包含 UNIX
的行。
deleting lines
sed '1,2d' intro
删除前两行。sed /UNIX/d intro
删除包含 UNIX
的行,其实并不是删除,是过滤掉这些行。
tr
translate or delete characters,tr from-chars to-chars
,from-chars
和 to-chars
是一个或多个或者一些字符的集合,任何出现在 from-chars
中的字符都会被变成对应 to-chars
位置上的字符。
tr e x < intro
,tr
的输入必须要从文件中重定向,因为 tr
是从标准输入读取的,结果输出到标准输出,且源文件不变。
cut -d: -f1,6 /etc/passwd | tr : '\t'
,如果想使用不可打印字符,可以在 tr
中使用八进制表示。
date | tr ' ' '\12'
。
tr
能转换一系列的字符,tr '[a-z]' '[A-Z]' < intro
把文件中所有小写字符都替换为大写字符。
tr '[a-zA-Z]' '[A-Za-z]'
大写变小写,小写变大写。
-s
Option
使用 -s
可以去掉连续多次出现在 to-chars
中的字符,如果用 to-chars
替换后的连续多次重复出现,则使用单个字符替换。
tr -s ':' '\11'
把所有 colons
转变成 tab
,使用单个 tab
替换多个,所以输入中一次或多次出现的 colons
会被一个 tab
替代。
this is good
,tr -s ' ' ' ' < lotsaspaces
。
-d
Option
tr -d from-chars
删除 from-chars
中的字符。 tr -d ' ' < intro
删除所有空格。
同样可以使用 sed 's/ //g' intro
来达到相同的效果,解决方式很多,但是这里 tr
更好点,它更小更快。
tr '()' '{}'
把左括号变成左花括号,右括号变成右花括号。
grep
grep
允许你搜索一个或多个文件来查找指定内容。grep pattern files
。
每个文件的每一行只要包含来匹配模式都会被输出到标准输出。
grep * file
和 grep '*' file
是不同的,grep -i 'the' intro
,-i
忽略大小写。
grep 可以接收正则表达式
。
-v
Option
grep -v 'UNIX' intro
,不包含 UNIX
的行。
-l
Option
有时希望找到哪些文件包含特定的字符,而不对具体行感兴趣。
grep -l 'Move_history' *.c
找到所有包含 Move_history
的文件。
-n
Option
grep -n 'Move_history' testch.c
打印匹配到的行的行号。
sort
sort names
默认升序排序。
-u
去掉重复的行,早期的 Unix 使用者通常使用另外一个工具实现相同的效果 sort | uniq
。
-r
反向排序。 -n
把行当作数值处理在排序。 -k2n
k
== skip
,跳过某个字段,-k2n
意味着从以第二列为排序列,默认使用 \t
分割行,可以使用 -t
指定分隔符。
sort -k3n -t: /etc/passwd
sort by user id。
uniq
uniq in_file out_file
,把输入文件中有重复的行去掉,在输出到第二个文件。uniq
只会找相连的行来判断是否重复,所以一般要和 sort
一起使用。
sort names | uniq
。
-d
找到重复的行,sort names | uniq -d
列出重复的行。
sort /etc/passwd | cut -f1 -d: | uniq -d
找到重复的用户。
-c
列出重复出现的次数。
tr '[A-Z]' '[a-z]' datafile | sort | uniq -c | head
找出数据文件中出现最多的单词。