主要内容
正则表达式
正则表达式的作用是通过使用特定的辅助字符来对文本中的数据进行模糊匹配。正则表达式的类型有两种:一种是基本正则表达式,另一种是扩展正则表达式。扩展正则表达式是对基本正则表达式的增强,它在基本正则表达式的基础上增加了一些辅助字符。正则表达式常见的字符是星号(*),例如我们通过*
号来匹配任意字符,但是在不同的命令使用星号,它的作用不完全等同。
特殊字符
基本正则表达式特殊字符: . * [] ^ $ \
扩展正则表达式特殊字符: + ? | () {}
特殊字符只用在匹配规则上面,如果这些字符是要被匹配的字符,则需要通过转义字符转义。
基本正则表达式(BRE)
Linux系统上面的大部分文本处理工具都支持正则表达式,但是,不同的命令对正则特殊字符的理解不完全相同,这个需要注意,例如最常用的星号(*)和ls命令。假如有bat,cat和hat三个文件,我只希望查看是否有这三个文件,有两种方式:
第一种是使用星号:
1 2 |
dns@DESKTOP-LBH7QGM:~/tmp$ ls *at bat cat hat |
另一种是是使用方括号:
1 2 |
dns@DESKTOP-LBH7QGM:~/tmp$ ls [bch]at bat cat hat |
使用星号的通用性更大,因为星号匹配任意字符(零个或者多个)。而第二种方式的方括号的作用是包含其中任意一个的意思,[bah]的意思是:包含b、a、h任意一个字符。
星号是最简单的正则匹配。
grep命令
另一个和正则表达式密切相关的文本处理工具是grep,它的作用是通过指定匹配模型来对文本进行搜索并打印到基本输出,这个匹配模型就是一个正则表达式。
例如:
1 2 |
dns@DESKTOP-LBH7QGM:~/tmp$ grep "root" /etc/passwd root:x:0:0:root:/root:/bin/bash |
上面的例子查找/etc/passwd文件中包含root的文本行,如果有则打印到屏幕。
点号(.)和星号(*)
点号是匹配一个任意字符(一定有一个字符,且只有一个字符),而星号是匹配零次或多次前导字符。
举个例子,test文件中有如下内容:
1 2 3 4 5 6 7 |
bat cat hat but boot boast bt |
如果我只希望列出文件中的bat和but,则可以使用下面的命令:
1 2 3 |
dns@DESKTOP-LBH7QGM:~/tmp$ grep "b.t" test.txt bat but |
b.t的意思是,b和t之间有且只有一个字符。
如果希望b和t之间不管有没有东西的都要把它列出来,则可以这样:
1 2 3 4 5 6 |
dns@DESKTOP-LBH7QGM:~/tmp$ grep "b.*t" test.txt bat boot boast but bt |
这样,无论b和t之间有没有其他字符的,都会被显示出来。因为(.)是匹配一个字符,而(*)是匹配任意前导字符,它们结合在一起的意思是:匹配零个或者多个任意字符。
在使用星号的时候需要注意,shell中文件名匹配时星号的意思是匹配任意字符;除文件名匹配外,星号代表的意思是,匹配前导字符零次或者多次。一个很容易用错的例子是:
查找/etc/passwd文件中使用的shell是nologin的用户
1 2 3 4 5 6 |
dns@DESKTOP-LBH7QGM:~/tmp$ grep "nologin" /etc/passwd daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin ... games:x:5:60:games:/usr/games:/usr/sbin/nologin |
这是一个正确的例子,所有nologin的用户都被列出。下面是错误的例子:
1 2 |
dns@DESKTOP-LBH7QGM:~/tmp$ grep "*nologin" /etc/passwd dns@DESKTOP-LBH7QGM:~/tmp$ |
这一次没有任何输出,原因是,星号依赖于它的前导字符,如果星号前面没有前导字符,无论任何时候,它都不能匹配任何东西,因此,不要把星号放到正则表达式的前面。
锚点操作符^和$
^
符号用于匹配文本行的开头,而$
相反,它是匹配文本行的结尾。例如:
1 2 3 |
[centos7-server ~]# grep "root" /etc/passwd root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin |
如果不使用锚点操作符,所有包含root的行都被输出。现在,我只想列出以root开头的文本行:
1 2 |
[centos7-server ~]# grep "^root" /etc/passwd root:x:0:0:root:/root:/bin/bash |
如果希望查看/etc/passwd文件中谁使用了shell,不管是bash还是zsh列出来,可以这样:
1 2 3 |
[centos7-server ~]# grep "sh$" /etc/passwd root:x:0:0:root:/root:/bin/bash normal:x:1000:1000:normal:/home/normal:/bin/zsh |
如果不使用$
符号,则所有包含sh的行都被列出,这样并不是我们要的结果:
1 2 3 4 5 6 7 |
[centos7-server ~]# grep "sh" /etc/passwd root:x:0:0:root:/root:/bin/bash shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown setroubleshoot:x:991:988::/var/lib/setroubleshoot:/sbin/nologin sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin normal:x:1000:1000:normal:/home/normal:/bin/zsh apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin |
另外,这两个符号组合使用还可以查找空行,在阅读配置文件的时候这个功能特别有用:
1 2 3 4 5 6 7 8 9 |
[centos7-server ~]# grep -v "^#" /etc/rsyslog.conf | grep -v "^$" $ModLoad imuxsock # provides support for local system logging (e.g. via logger command) $ModLoad imjournal # provides access to the systemd journal ... cron.* /var/log/cron *.emerg :omusrmsg:* uucp,news.crit /var/log/spooler local7.* /var/log/boot.log ... |
上例中省略了部分输出,grep先删除了注释行,即以#开头的行,然后再把空行(^$)删除,剩下的就是实际的配置行。
字符组 []
使用方括号([])可以指定一组字符,然后程序就可以匹配字符组中的任意一个字符。
例如:
1 2 3 4 5 6 7 8 9 |
[centos7-server ~]# cat test.txt bat cat hat dog [centos7-server ~]# grep "[bch]at" test.txt bat cat hat |
字符组取反
还可以对字符组中的字符进行取反,即匹配除字符组之外的字符
1 2 3 |
[centos7-server ~]# grep "[^b]at" test.txt cat hat |
除了b不匹配外,其他字符都匹配。
范围匹配
字符组中还可以指定一个或多个字符范围,例如[a-zA-Z0-9],但是,设置范围的时候必须是从小到大,否则会出现错误:[z-aZ-A9-0],这是不正确的。
转义特殊字符
有时候我们查找的可能是这些特殊字符的其中之一,这时就需要对特殊字符进行转义:
1 2 3 4 5 |
[centos7-server ~]# grep "$" test.txt The line without a dollar sign Last year 400,000 acres of land yielded a crop worth $1.75 billion. [centos7-server ~]# grep "\\$" test.txt Last year 400,000 acres of land yielded a crop worth $1.75 billion. |
例子中我们是想找出含有美元符号的一行,但由于$是特殊字符,我们必须对它进行转义,但是\本身也是特殊字符,因此,这里有两个\\,其中一个是对\本身的转义。
扩展正则表达式(ERE)
扩展的正则表达式在BRE的基础上添加了+ ? | () {}
这几个特殊字符。
在使用grep的时候要注意,默认grep不支持扩展正则表达式的语法,因此要使用-E参数开启支持,或者使用egrep。
使用问号(?)
问号的意思是匹配零次或者一次前导字符。
1 2 3 4 5 6 |
[centos7-server ~]# echo "bet" | grep -E "be?t" bet [centos7-server ~]# echo "bt" | grep -E "be?t" bt [centos7-server ~]# echo "beet" | grep -E "be?t" #e出现超过两次,匹配失败 [centos7-server ~]# |
问号还可以配合其他特殊字符使用,例如[]
。
1 2 3 4 5 |
[centos7-server ~]# echo "bet" | grep -E "b[ae]?t" bet #同点符号一样,问号也是最多只能匹配一个字符 [centos7-server ~]# echo "beat" | grep -E "b[ae]?t" [centos7-server ~]# |
使用加号(+)
加号的作用是:匹配前导字符一次或者多此,即前导字符至少出现一次。
1 2 3 4 5 6 |
[centos7-server ~]# echo "beet" | grep -E "be+t" beet [centos7-server ~]# echo "bet" | grep -E "be+t" bet [centos7-server ~]# echo "bt" | grep -E "be+t" #没有出现过一次e,匹配失败 [centos7-server ~]# |
加号也可以和[]
配合使用:
1 2 3 4 5 6 7 8 9 10 |
[centos7-server ~]# echo "beeat" | grep -E "b[ae]+t" beeat [centos7-server ~]# echo "beat" | grep -E "b[ae]+t" beat [centos7-server ~]# echo "bat" | grep -E "b[ae]+t" bat [centos7-server ~]# echo "bet" | grep -E "b[ae]+t" bet [centos7-server ~]# echo "bt" | grep -E "b[ae]+t" [centos7-server ~]# |
使用花括号{}
大括号有如下几种表示方式:
- {N}:前导字符出现N次
- {N,}:前导字符至少出现N次,或者更多
- {,M}:前导字符最多出现M次
- {N,M}:前导字符至少出现N次,最多M次。
1 2 3 4 5 |
[centos7-server ~]# echo "bt" | grep -E "be{1,}t" #至少一个e,但bt没有e,因此不匹配 [centos7-server ~]# echo "beet" | grep -E "be{1,3}" beet [centos7-server ~]# echo "bet" | grep -E "be{1,3}" bet |
使用管道操作符
管道操作符的作用类似于逻辑或||
,用于设置多个匹配条件,只匹配任意一个条件即可。
1 2 3 4 5 6 |
[centos7-server ~]# echo "I have a cat" | grep -E "cat|dog|mouse" I have a cat [centos7-server ~]# echo "I have a dog" | grep -E "cat|dog|mouse" I have a dog [centos7-server ~]# echo "I have a mouse" | grep -E "cat|dog|mouse" I have a mouse |
转载请注明:Pure nonsense » shell程序设计(八)正则表达式