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 找出数据文件中出现最多的单词。