简单来讲,正则表达式是处理字符串的方法,它是以行为单位来进行字符串的处理行为,正则表达式通过一些特殊符号的辅助,可以让用户轻易实现查找、删除、替换某特定字符串。
正则表达式基本上是一种“表示法”,只要工具程序支持这种表示方法,那么该工具程序就可以用来作为正则表达式的字符串处理之用。例如:vi、grep、awk、sed等工具,它们本身支持正则表达式,故可以使用正则表达式的特殊字符来进行字符串处理。但是例如ls、cp等命令并不支持正则表达式,所以只能使用bash自身的通配符。注意,通配符(*、?、[]、[-]、[^])并不是正则表达式,两者是不一样的概念。
换而言之,使用ls、cp等命令时,某些符号是做通配符来使用的;而使用vi、grep、awk、sed等工具时,某些符号是做正则表达式来使用的,两者的意义功能是不一样的。
正则表达式分为基础正则表达式和扩展正则表达式
特殊符号 | 代表意义 |
---|---|
[:alnum:] | 代表英文大小写字符及数字,即0-9,A-Z,a-z |
[:alpha:] | 代表英文大小写字符,即A-Z,a-z |
[:blank:] | 代表空格键与[Tab]按键 |
[:cntrl:] | 代表键盘上面的控制按键,包括CR(回车)、LF(换行)、Tab、Del等 |
[:digit:] | 代表数字,即0-9 |
[:graph:] | 除了空格键(空格键和[Tab]键)之外的其他所有按键 |
[:lower:] | 代表小写字符,即a-z |
[:print:] | 代表任何可以被打印出来的字符 |
[:punct:] | 代表标点符号(punctuation symbol),即" ’ ? ! ; : # $ |
[:upper:] | 代表大写字符,即A-Z |
[:space:] | 代表会产生空白的字符,包括空格键 [Tab] CR等 |
[:xdigit:] | 代表16进制的数字类型,包括0-9,A-F,a-f的数字与字符 |
实际上,最常用的莫过于[:alnum:]、[:alpha:]、[:upper:]、[:lower:]、[:digit:]。
用法:
grep [-A] [-B] [--color=auto] ‘搜寻字符串’ filename
参数:
-A 后面可加数字,表示after的意思,除了列出该行外,还将后续n行也列出来
-B 后面可加数字,表示before的意思,除了列出该行外,还将先前的n行也列出来
--color=auto 将符合字符串的用特殊颜色标识(不设也没关系,默认就是有颜色的)
例:
dmesg查看内核信息,|管道符,grep匹配eth相关字符所在行并输出,同时将找到行的前2行和后3行也输出,还标示行号,如下:
dmesg | grep -n -A3 -B2 'eth'
基础正则表达式特殊字符归纳表(假设文件为fileName.txt)
特殊符号 | 意义 | 范例 | |
---|---|---|---|
^word | 待查找的字符串word在行首 | 查找以#开始的行,并列出行号 | grep -n ‘^#’ fileName.txt |
word$ | 待查找的字符串word在行尾 | 将行尾为!的行输出,并列出行号 | grep -n ‘!$’ fileName.txt |
. | 代表一定有一个任意的字符 | 可以是aaa、aba、a a等,但不能是aa | grep -n ‘a.a’ fileName.txt |
\ | 转义字符,让特殊符号失去意义 | 查找含有单引号的那一行 | grep -n \’ fileName.txt |
* | 重复0个到无穷多个前一个字符 | 可以是sb、sbb、sbbb等字符 | grep -n ‘sbb*’ fileName.txt |
[list] | 代表1个列表中的字符 | 查找mgh、mgq、mgy | grep -n ‘mg[hqy]’ fileName.txt |
[n1-n2] | 代表字符范围内的1个字符 | 查找class1、class2……class9 | grep -n ‘class[1-9]’ fileName.txt |
[^list] | 不是列表中的1个字符 | m后接1个字符,但不能是mb、mc | grep -n ‘m[^bc]’ fileName.txt |
\{n,m\} | 连续n-m个前一个字符 | 可以是moon、mooon | grep -n ‘mo\{2,3\}n’ fileName.txt |
注意:
在正则表达式中,*(asterisk星号)的用法比较诡异,其代表重复0到无穷多个前一个字符,所以如果要查找sb、sbb、sbbb,即至少有1个b的字符,那么实际上你要敲成sbb* 才行的,即至少写俩b才行,因为sbb*代表sb之后紧接着有0到无穷个的b存在!!
而在通配符中,*(asterisk星号)则表示0到无限多个任意字符的意思,两者意义不同,不可混淆。
^ 符号在中括号外面,即^[list]表示行首为list中字符打头;而 ^ 符号在中括号里面,即[^list]表示反选list中字符。
先写个testFile.txt来做grep查找,文件内容如下
God bless me!
Oh, my god!
What's your name?
Happy new year!
Goodbye.
Time to say goodbye.
Goood evening.
Gooood morning.
例1-查找特定字符串:
grep -n 'me' testFile.txt # 查找含字符串'me'的行,并显示行号,结果如下:
1:God bless me!
3:What's your name?
7:Time to say goodbye.
grep -n 'god' testFile.txt # 查找含字符串'god'的行,如下(注意是区分大小写的):
2:Oh, my god!
grep -in 'god' testFile.txt # 不区分大小写查找含字符串'god'的行,加-i即可,如下:
1:God bless me!
2:Oh, my god!
grep -vn 'm' testFile.txt # 查找不含'm'字符串的行,如下:
4:Happy new year!
5:
6:Goodbye.
8:Goood evening.
例2-利用中括号[]查找列表中的字符
grep -n 'm[ey]' testFile.txt # 查找含字符串'me'或'my'的行,如下:
1:God bless me!
2:Oh, my god!
3:What's your name?
7:Time to say goodbye.
grep -n 'm[^ey]' testFile.txt # 查找含m但是后面跟的不是e或y的字符串的行,如下:
9:Gooood morning.
grep -n '[^A-Z]oo' testFile.txt # 查找含oo但前面不是大写字母的字符串的行,如下:
7:Time to say goodbye.
8:Goood evening.
9:Gooood morning.
上例中的第8-9行是因为oo前面跟的是o,未必是G,所以不能排除掉。
例3-行首字符^和行尾字符$
grep -n '^G' testFile.txt # 查找行首G开头的行,如下:
1:God bless me!
6:Goodbye.
8:Goood evening.
9:Gooood morning.
grep -n '!$' testFile.txt # 查找行尾!结束的行,如下:
1:God bless me!
2:Oh, my god!
4:Happy new year!
grep -n '^[OW]' testFile.txt # 行首用O或W打头,如下:
2:Oh, my god!
3:What's your name?
grep -n '^$' testFile.txt # ^$连用表示行首接着行尾,即空白行,如下:
5:
grep -v '^$' testFile.txt | grep -v '^#' # 这个用的较多,把bash脚本中空白行和#开头的注释行都刨掉
例4-任意1个字符. 和 0个到无穷个前面字符的重复*
grep -n 'g..d' testFile.txt # 查找g开头d结尾中间俩任意字符的字符串,如下:
7:Time to say goodbye.
grep -n 'ooo*' testFile.txt # 查找含两个o以上的字符串,要写3个o才行的,如下:
6:Goodbye.
7:Time to say goodbye.
8:Goood evening.
9:Gooood morning.
例5-限定连续字符范围{}(用\转义,故写作{})
grep -n 'o\{2\}' testFile.txt # 查找含2个连续o的字符串,如下:
6:Goodbye.
7:Time to say goodbye.
8:Goood evening.
9:Gooood morning.
grep -n 'o\{3,5\}' testFile.txt # 查找含3-5个连续o的字符串,如下:
8:Goood evening.
9:Gooood morning.
grep -n 'o\{4,\}' testFile.txt # 查找含4个及以上连续o的字符串,如下:
9:Gooood morning.
sed本身是一个管道命令,其可以分析standard input的,而且sed还可以将数据进行替换、删除、新增、选取特定行等的功能。
用法:
sed [-nefr] [动作] fileName
选项:
-n 使用安静模式,一般sed使用时,所有来自stdin的数据都会输出到屏幕上,但是用了安静模式后,只有经过sed处理的特殊行才会被列出来。注意这个-n不是加行号,而是安静模式,悄没声滴,啥也不显摆。
-e 直接在命令行模式上进行sed的动作编辑(默认就是-e的,所以不用添加)。
-f 直接将sed的动作写在一个文件内,-f filename则可以执行filename内的sed动作(相当于把sed的动作写到文件里,然后执行该文件就可以了,好像用不着这么麻烦哈)。
-r sed的动作支持的是扩展正则表达式的语法(默认是基础正则表达式的语法)。
-i 直接修改读取的文件内容,而不是由屏幕输出(默认是不修改源文件,只显示在命令行界面里的,要想修改源文件需要用-i选项)。
动作说明:[n1[,n2]]function
n1, n2 不一定存在,一般代表动作执行的行数,例如,若动作需要在10-20行之间进行,则“10,20[动作行为]”
function有下面参数可选:
a 新增,a的后面可以接字符串,这些字符串会在新的一行出现(当前行的下一行)
c 替换,c的后面可以接字符串,这些字符串将替换n1-n2之间的行
d 删除,后面不用接任何参数,因为直接删掉了
i 插入,后面可以接字符串,而这些字符串会在新的一行出现(当前行的上一行)
p 打印,将某个选择的数据打印出来,通常p会和参数sed -n一起运行(不然它会把源文件打印出来,再打印选择的数据,比较烦人)
s 替换,可以直接替换的工作。通常s会搭配正则表达式,例如:1,20s/old/new/g
还以前面的测试文件testFile.txt为例来做说明,文件内容如下:
God bless me!
Oh, my god!
What's your name?
Happy new year!
Goodbye.
Time to say goodbye.
Goood evening.
Gooood morning.
例1-删除指定行:
sed '2,8d' testFile.txt # 删除testFile.txt的2-8行之后再显示,如下:
God bless me!
Gooood morning.
sed -n '2,8d' testFile.txt # 如果想显示行号,不能用-n,-n是安静模式,加上就啥也不输出了。
cat -n testFile.txt | sed '2,8d' # 如果想显示行号,可以用管道符来整,如下:
1 God bless me!
9 Gooood morning.
例2-新增行
cat -n testFile.txt | sed '3a Hello World!' # 在第3行后面增加一行‘Hello World!’,如下:
1 God bless me!
2 Oh, my god!
3 What's your name?
Hello World!
4 Happy new year!
5
6 Goodbye.
7 Time to say goodbye.
8 Goood evening.
9 Gooood morning.
cat -n testFile.txt | sed '3i Hello World!' # 在第3行前面增加一行‘Hello World!’,如下:
1 God bless me!
2 Oh, my god!
Hello World!
3 What's your name?
4 Happy new year!
5
6 Goodbye.
7 Time to say goodbye.
8 Goood evening.
9 Gooood morning.
如果要增加多行,用反斜杠然后回车就可以换行了(相当于把回车符转义了):
cat -n testFile.txt | sed '3a Hello World! \
> Nice to meet you!' # 回车后显示如下:
1 God bless me!
2 Oh, my god!
3 What's your name?
Hello World!
Nice to meet you!
4 Happy new year!
5
6 Goodbye.
7 Time to say goodbye.
8 Goood evening.
9 Gooood morning.
例3-替换行:
cat -n testFile.txt | sed '2,8c Delete 2-8 lines.' # 将2-8行替换为Delete 2-8 lines.
1 God bless me!
Delete 2-8 lines.
9 Gooood morning.
例4-只打印想要的行:
注意,加了-n安静模式,不然的话,下面的打印会依照1,2,3,3,4,4,5,6,7,8,9行的顺序打印一遍。
cat -n testFile.txt | sed -n '3,4p' # 只显示3-4行内容,如下:
3 What's your name?
4 Happy new year!
例5-查找替换:
sed ‘s/旧字符串/新字符串/g’
cat -n testFile.txt | sed 's/oooo*/oo/g' # 把三个及以上连续o的字符串替换成两个连续o,如下:
1 God bless me!
2 Oh, my god!
3 What's your name?
4 Happy new year!
5
6 Goodbye.
7 Time to say goodbye.
8 Good evening.
9 Good morning.
例6-直接修改源文件:
默认的sed操作只是显示在屏幕上,不针对源文件,但是如果想直接修改源文件的话,那么加上-i选项就可以了。
sed 's/oooo*/oo/g' testFile.txt # 把大于等于3个连续o的字符串替换成oo,直接修改源文件,无任何显示!
cat testFile.txt # 查看源文件,发现的确被更改了,如下:
God bless me!
Oh, my god!
What's your name?
Happy new year!
Goodbye.
Time to say goodbye.
Good evening.
Good morning.
例7-执行文件中写好的sed动作:
用-f可以把事先写好的sed动作拿来执行,比如先新建个文件sedCom,用vi编辑其内容为/s/ me/ you/g,即sed的替换动作,注意不要加单引号,然后sed -f sedCom执行就可以了。
cat -n testFile.txt | sed -f sedCom # 用文件来制定sed动作,执行结果如下,成功替换:
1 God bless you!
2 Oh, my god!
3 What's your name?
4 Happy new year!
5
6 Goodbye.
7 Time to say goodbye.
8 Good evening.
9 Good morning.
用grep命令的时候,如果要同时刨掉空白行和以#开始的注释行,用基础表达式的话,只能写成如下形式:
grep -v '^$' fileName | grep -v '^#'
如果用扩展表达式,就方便多了,注意grep默认只支持基础正则表达式,若想使用扩展正则表达式,需要加上-E选项,即grep -E或者直接用egrep命令就好了。
grep -Ev '^$|^#' fileName
egrep '^$|^#' fileName
上面单引号里的竖杠杠 | 表示 逻辑或 的意思,属于扩展正则表达式。
扩展正则表达式特殊字符归纳表(假设文件为fileName.txt)
符号 | 意义 | 范例 | |
---|---|---|---|
+ | 重复1个或1个以上的前一个字符 | 可以是god、good、goood等,但非gd | egrep -n ‘go+d’ fileName.txt |
? | 0个或1个前一个字符 | 可以是gd、god,但非good、goood等 | egrep -n ‘go?d’ fileName.txt |
| | 用或or的方式找出多个字符串 | 可以是gd、god、good | egrep -n 'gd |
() | 找出‘组’字符串 | 可以是glad、good,la和oo竖杠隔开 | egrep -n ‘g(la|oo)d’ fileName.txt |
()+ | 多个重复组的判别 | 可以为ILoveLoveLoveU,Love重复多次 | egrep -n ‘I(Love)+U’ fileName.txt |
用法:
printf ‘打印格式’ 实际内容
参数:
关于格式方面的几个特殊样式
\a 输出警告声音
\b 退格键backspace
\f 清除屏幕form feed换页符
\n 输出新的一行
\r 回车键Enter
\t 水平tab键
\v 垂直tab键
\xNN NN为两位的数字,可以转化数字为字符
关于c语言内,常见的变量格式
%ns n为数字,s代表字符string,即n位字符串
%ni n为数字,i为整形integer,即n位的整数
%N.nf n和N都是数字,f代表浮点floating,如果有小数数字,总共10位,小数点后保留两位输出,则写成%10.2f就可以了
例,假设有个成绩单文件score,内容如下:
Name English Chinese Math Total Aver
MeiGuanhua 99 98 95 292 97.33
LiLi 88 86 82 256 85.33
让其做格式输出
printf '%s\t %s\t %s\t %s\t %s\t %s\t \n' $(cat score) # 显示如下:
Name English Chinese Math Total Aver
MeiGuanhua 99 98 95 292 97.33
LiLi 88 86 82 256 85.33
tab的对齐压根就没对齐,而且平均分我只想看1位小数,所以还要处理下才好
printf '%12s %8s %8s %8s %8s %8s \n' $(cat score | sed -n '1p'); \
printf '%12s %8i %8i %8i %8i %8.1f \n' $(cat score | sed -n '2,3p') # 如下:
Name English Chinese Math Total Aver
MeiGuanhua 99 98 95 292 97.3
LiLi 88 86 82 256 85.3
用法:
awk ‘条件类型 1{动作 1} 条件类型 2{动作 2} …’ filename
awk是一列一列来折腾的,默认的分隔符是空格或tab,注意其会把连续的空格算成是一个空格,这点跟之前的cut很不一样,awk人性化多了。
例,还是上面的成绩单文件,但是我只想看到姓名和总成绩的栏,就可以用awk来处理了:
awk '{print $1 "\t" $5}' score
Name Total
MeiGuanhua 292
LiLi 256
上例将awk和print(注意是print不是printf)联合使用来输出特定分隔的字符串,字符串默认是用空格(连续空格)或tab来分隔的,其中的$1、$2、$3……分别表示第1、第2、第3……个字符串,还有个$0表示这一行所有的字符串。
由于awk的后续动作是要用单引号括起来的,所以如果用print(不是printf)命令的话,如果要打印不是变量的字符或者格式的话,用双引号括起来就好了,比如上例的"\t"。
awk命令中还有3个内置变量,NF、NR、FS分别表示每一行($0)拥有的字段总数、当前awk处理的是第几行、当前的分隔字符(默认是空格键)。
例-输出NF、NR、FS看看:
awk '{print $5 "\t current line: " NR "\t total colume: " NF, "division char:", FS, "."}' score # 显示如下:
Total current line: 1 total colume: 6 division char: .
292 current line: 2 total colume: 6 division char: .
256 current line: 3 total colume: 6 division char: .
awk的逻辑运算符
符号 | 意义 |
---|---|
> | 大于 |
< | 小于 |
>= | 大于或等于 |
<= | 小于或等于 |
== | 等于 |
!= | 不等于 |
例-看看语文分数大于等于90分的是谁:
awk '$3>=90 {print $1 "\t" $3}' score # 如下:
Name Chinese
MeiGuanhua 98
之所以第一行Chinese也输出来了,是因为它把字符串跟90作比较了,不然要输出小于90的,它就不输出第1行了。
当然,如果用printf的话,可以在双引号里面用%s、%i、%f来指明输出类型了。
awk '$3>90 {printf "%10s %10s\n", $1, $3}' score # 显示如下
Name Chinese
MeiGuanhua 98
diff用于两个文件的比较,其以行为单位进行比较,通常用在同一文件(或软件)的新旧版本的区别上!
用法:
diff [-bBi] from-file to-file
其中from-file为欲比较的文件,而to-file为目标比较文件。
参数:
-b 忽略一行中仅有多个空白的区别
-B 忽略空白行的区别
-i 忽略大小写的区别
cmp是比较俩文件的“字节”,当然也可以比较二进制文件。
用法:
cmp [-s] file1 file2
参数:
-s 将所有的不同点的字节都列出来,因为cmp默认仅会输出第1个发现的不同点。
patch与diff密切相关,其常用于比较新旧两个文件,然后制作补丁文件,将旧文件升级为新文件。