linux正则表达式
linux下的正则表达式
1、字符匹配元字符
. 任意单个字符,包含汉字,一个汉字也是一个字符
[wang] 指定范围的字符
[^wang] 不在指定范围的字符
[:alnum:] 字母和数字
[:alpha:] 代表任何英文大小写字符,亦即 A-Z, a-z
[:lower:] 小写字母,示例:[[:lower:]],相当于[a-z]
[:upper:] 大写字母
[:blank:] 空白字符(空格和制表符)
[:space:] 水平和垂直的空白字符(比[:blank:]包含的范围广)
[:cntrl:] 不可打印的控制字符(退格、删除、警铃...)
[:digit:] 十进制数字
[:xdigit:]十六进制数字
[:graph:] 可打印的非空白字符
[:print:] 可打印字符
[:punct:] 标点符号
yang@yang-user:~$ grep root /etc/passwd
root:x:0:0:root:/root:/bin/bash
nm-openvpn:x:120:126:NetworkManager OpenVPN,,,:/var/lib/openvpn/chroot:/usr/sbin/nologin
# “.”匹配任意单个字符
yang@yang-user:~$ grep r..t /etc/passwd
root:x:0:0:root:/root:/bin/bash
nm-openvpn:x:120:126:NetworkManager OpenVPN,,,:/var/lib/openvpn/chroot:/usr/sbin/nologin
# 一个“.”代表一个字符
yang@yang-user:~$ echo roter |grep r..t
yang@yang-user:~$
# 也可以是汉字
yang@yang-user:~$ echo r测试ter |grep r..t
r测试ter
[ ] 指定范围的字符
# 会将检索到的字符给标红显示
yang@yang-user:~$ echo abc |grep '[abcd]'
abc
yang@yang-user:~$ echo abcd |grep '[ab]'
abcd
yang@yang-user:~$ echo ab cd |grep '[ab]'
ab cd
yang@yang-user:~$ echo a bcd |grep '[ab]'
a bcd
[^root]不在指定范围的字符
# 取反,cd会标红显示
yang@yang-user:~$ echo a bcd |grep '[^ab]'
a bcd
例1:
# rc匹配“.”或者0-6的数字,[]只要包含的都会去匹配
[root@centos8 ~]#ls /etc/ | grep 'rc[.0-6]'
rc0.d
rc1.d
rc2.d
rc3.d
rc4.d
rc5.d
rc6.d
rc.d
rc.local
# 最后一个点表示任意的字符,和放[]里边含义不同,放里边就是点本身
[root@centos8 ~]#ls /etc/ | grep 'rc[.0-6].'
rc0.d
rc1.d
rc2.d
rc3.d
rc4.d
rc5.d
rc6.d
rc.d
rc.local
# 如果想表示点本身,就需要加转移符了
[root@centos8 ~]#ls /etc/ | grep 'rc[.0-6]\.'
rc0.d
rc1.d
rc2.d
rc3.d
rc4.d
rc5.d
rc6.d
解释:“.”表示任意单个字符,0-6 匹配 0-6,[.0-6]的意思是可以匹配任意单个字符,包含“.”本身,后边 0-6 就是匹配 0 到 6
2、匹配次数
用在要指定次数的字符后面,用于指定前面的字符要出现的次数
* 匹配前面的字符任意次,包括0次,贪婪模式:尽可能长的匹配
.* 任意长度的任意字符
\? 匹配其前面的字符0或1次,即:可有可无
\+ 匹配其前面的字符至少1次,即:肯定有,>=1
\{n\} 匹配前面的字符n次
\{m,n\} 匹配前面的字符至少m次,至多n次
\{,n\} 匹配前面的字符至多n次,<=n
\{n,\} 匹配前面的字符至少n次
* 匹配前面的字符任意次,包括0次,贪婪模式:尽可能长的匹配
# 匹配*之前的“o”出现任意次,包含0次,只配合“*”前边的字符
yang@yang-user:~$ echo google |grep 'go*gle'
google
yang@yang-user:~$ echo gogle |grep 'go*gle'
gogle
yang@yang-user:~$ echo ggle |grep 'go*gle'
ggle
.* 任意长度的任意字符
# .* 前边的任意长度的任意字符
yang@yang-user:~$ echo ggle |grep 'go.*gle'
yang@yang-user:~$ echo ggle |grep 'g.*gle'
ggle
\? 匹配其前面的字符0或1次,即:可有可无
# 匹配0次,符合要求
yang@yang-user:~$ echo ggle |grep 'go\?gle'
ggle
# 匹配1次,符合要求
yang@yang-user:~$ echo gogle |grep 'go\?gle'
gogle
# 匹配2次,不符合要求
yang@yang-user:~$ echo google |grep 'go\?gle'
yang@yang-user:~$
\+ 匹配其前面的字符至少1次,即:肯定有,>=1
# “o”出现两次符合要求
yang@yang-user:~$ echo google |grep 'go\+gle'
google
# “o”出现一次符合要求
yang@yang-user:~$ echo gogle |grep 'go\+gle'
gogle
# “o”出现0次不符合要求,必须大于等于1
yang@yang-user:~$ echo ggle |grep 'go\+gle'
yang@yang-user:~$
\{n\} 匹配前面的字符n次
# 匹配0次匹配不到
yang@yang-user:~$ echo ggle |grep 'go\{2\}gle'
# 匹配2次匹配到了
yang@yang-user:~$ echo google |grep 'go\{2\}gle'
google
# 匹配3次匹配不到
yang@yang-user:~$ echo gooogle |grep 'go\{2\}gle'
\{m,n\} 匹配前面的字符至少m次,至多n次,即>=m,<=n
# 匹配1次匹配不到
yang@yang-user:~$ echo gogle |grep 'go\{2,4\}gle'
# 匹配2次匹配到了
yang@yang-user:~$ echo google |grep 'go\{2,4\}gle'
google
# 匹配4次匹配到了
yang@yang-user:~$ echo goooogle |grep 'go\{2,4\}gle'
goooogle
# 匹配5次匹配不到
yang@yang-user:~$ echo gooooogle |grep 'go\{2,4\}gle'
yang@yang-user:~$
\{,n\} 匹配前面的字符至多n次,<=n
# 即配置的值最大为2,可以是0次,不可以是3次
yang@yang-user:~$ echo ggle |grep 'go\{,2\}gle'
ggle
yang@yang-user:~$ echo gogle |grep 'go\{,2\}gle'
gogle
yang@yang-user:~$ echo google |grep 'go\{,2\}gle'
google
yang@yang-user:~$ echo gooogle |grep 'go\{,2\}gle'
yang@yang-user:~$
\{n,\} 匹配前面的字符至少n次
# 匹配次数大于等于2的可以匹配到
yang@yang-user:~$ echo gooogle |grep 'go\{2,\}gle'
gooogle
# 匹配次数小于2的匹配不到
yang@yang-user:~$ echo gogle |grep 'go\{2,\}gle'
yang@yang-user:~$
3、位置锚定
位置锚定可以用于定位出现的位置
^ 行首锚定,用于模式的最左侧
$ 行尾锚定,用于模式的最右侧
^PATTERN$ 用于模式匹配整行
^$ 空行
^[[:space:]]*$ 空白行
\< 或 \b 词首锚定,用于单词模式的左侧
\> 或 \b 词尾锚定,用于单词模式的右侧
\<PATTERN\> 匹配整个单词
例1:$ 行尾锚定,用于模式的最右侧
# 注意:$一定要放在bash后边
yang@yang-user:~$ grep bash$ /etc/passwd
root:x:0:0:root:/root:/bin/bash
yang:x:1000:1000:yang,,,:/home/yang:/bin/bash
例2:排除掉空行和#开头的行
yang@yang-user:~/tmp$ cat test1
#! /bin/bash
echo "hollo"
echo "test"
echo "this is test1"
#echo "this is test2"
echo "this is test3"
# 先过滤掉空行,然后在过滤掉注释行
yang@yang-user:~/tmp$ grep -v "^$" test1 |grep -v "^#"
echo "hollo"
echo "test"
echo "this is test1"
echo "this is test3"
其他写法
# 写法1:
yang@yang-user:~/tmp$ grep -v '^#\|^$' test1
echo "hollo"
echo "test"
echo "this is test1"
echo "this is test3"
# 写法2:
yang@yang-user:~/tmp$ grep -v '^\(#\|$\)' test1
echo "hollo"
echo "test"
echo "this is test1"
echo "this is test3"
# 写法3:
yang@yang-user:~/tmp$ grep "^[^#]" test1
echo "hollo"
echo "test"
echo "this is test1"
echo "this is test3"
\< 或 \b 词首锚定,用于单词模式的左侧
以什么开头的单词
# 匹配以root开头的
yang@yang-user:~$ echo root |grep '\<root'
root
yang@yang-user:~$ echo rooter |grep '\<root'
rooter
yang@yang-user:~$ echo Rooter |grep '\<root'
yang@yang-user:~$ echo aaarooter |grep '\<root'
yang@yang-user:~$
yang@yang-user:~$ echo root:er |grep '\<root'
root:er
\> 或 \b 词尾锚定,用于单词模式的右侧
# 匹配以root结尾的单词
yang@yang-user:~$ echo aaaroot |grep 'root\>'
aaaroot
yang@yang-user:~$ echo rooter |grep 'root\>'
yang@yang-user:~$
\<PATTERN\> 匹配整个单词
yang@yang-user:~$ echo root |grep '\<root\>'
root
yang@yang-user:~$ echo aaaroot |grep '\<root\>'
yang@yang-user:~$ echo rooter |grep '\<root\>'
例1:
# 统计磁盘的百分比,我们只需要关注以/dev/sd开头的即可,其他的都是内存,不需要关注
yang@yang-user:~$ df -h
文件系统 容量 已用 可用 已用% 挂载点
tmpfs 388M 1.8M 387M 1% /run
/dev/sda2 457G 14G 421G 4% /
tmpfs 1.9G 0 1.9G 0% /dev/shm
tmpfs 5.0M 4.0K 5.0M 1% /run/lock
/dev/sda1 511M 5.3M 506M 2% /boot/efi
tmpfs 388M 4.7M 384M 2% /run/user/1000
# 可以分步进行,第一步已先过滤出来已/dev/sd开头的
yang@yang-user:~$ df |grep '^/dev/sd'
/dev/sda2 479079112 13692628 440977092 4% /
/dev/sda1 523244 5364 517880 2% /boot/efi
# 第二步在过滤出来百分比那一列,grep -o参数是将能匹配到的全部打印出来,百分比都是数字,
# 用[0-9]匹配,\{1,3\}表示匹配前一位也就是数字0-9,最少匹配1次,最多匹配3次,因为百分比最少是1位,最多是3位
# 但是这样表示也不严谨,不过用在这个df的场景是可以的,为什么说不够严谨,因为[0-9]可以是0-9的任意数字,
# 但是百分比最多是100%,也就是说最多是1开头的,不会是其他数字
yang@yang-user:~$ df |grep '^/dev/sd' | grep -o '[0-9]\{1,3\}%'
4%
2%
4、分组其它
分组
分组:() 将多个字符捆绑在一起,当作一个整体处理。
例1:
# 代表\前的字符匹配三次,也就是说匹配c匹配三次,所以匹配不到
yang@yang-user:~$ echo abcabcabc |grep 'abc\{3\}'
# 想将abc作为一个整体进行匹配,就需要用到\(root\)这种写法了,这种是固定写法,不要理解成"\"为转义符了
yang@yang-user:~$ echo abcabcabc |grep '\(abc\)\{3\}'
abcabcabc
() 分组
后向引用:\1, \2, ...
| 或者
a|b #a或b
C|cat #C或cat
(C|c)at #Cat或cat
4.1、后向引用
前边作为一个整体被用到,后边还要用到这个整体,就不用在写\(m,n\)了,直接用"\1"代替,如果有多个,可以直接用"\2、\3…"代替
例1:
# 想将test3文件中的以r开头的,以t结尾的,中间有两个字符的单词替换成本体不变,前边加上abc后边加er,
# 也就说不管是root还是rabt,本体都不变,前边直接加上abc后边直接加上er
yang@yang-user:~/tmp$ vim test3
root root root
rabt rabt rabt
root
# 全局替换直接用%s///g的格式替换,或者“/”也可以换成###都可以,这里是匹配以r开头,中间是任意两个字符,以t结尾的单词,
# 在前边加上abc,后边机上er,以r口头以t结尾的共有两个,root和rabt,将这两个都全局替换了,这俩用到了后向引用“\1”替换结果如以下结果
%s#\(r..t\)#\abc\1er#g
yang@yang-user:~/tmp$ cat test3
abcrooter abcrooter abcrooter
abcrabter abcrabter abcrabter
abcrooter
例2:将文件中所有以“#”开头的行将“#”删除掉,也就是取消所有注释行
yang@yang-user:~/tmp$ vim fstab
# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# <file system> <mount point> <type> <options> <dump> <pass>
# / was on /dev/sda2 during installation
UUID=0c6a8991-acef-4f2c-83bc-72f74b7bce4d / ext4 errors=remount-ro 0 1
# /boot/efi was on /dev/sda1 during installation
UUID=B91C-053A /boot/efi vfat umask=0077 0 1
/swapfile
# 将以“#”开头的去掉“#”号,用到了后向引用
:%s/^#\(.*\)/\1/g
# 第二种写法,直接将#替换成空,也就相当于删除了#号
:%s/#//g
结果如下:
# 结果如下:
yang@yang-user:~/tmp$ vim fstab
/etc/fstab: static file system information.
Use 'blkid' to print the universally unique identifier for a
device; this may be used with UUID= as a more robust way to name devices
that works even if disks are added and removed. See fstab(5).
<file system> <mount point> <type> <options> <dump> <pass>
/ was on /dev/sda2 during installation
UUID=0c6a8991-acef-4f2c-83bc-72f74b7bce4d / ext4 errors=remount-ro 0 1
/boot/efi was on /dev/sda1 during installation
UUID=B91C-053A /boot/efi vfat umask=0077 0 1
/swapfile
例3:将以UUID开头的行前边加上#号,也就是注释掉以UUID开头的行
yang@yang-user:~/tmp$ vim fstab
# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# <file system> <mount point> <type> <options> <dump> <pass>
# / was on /dev/sda2 during installation
UUID=0c6a8991-acef-4f2c-83bc-72f74b7bce4d / ext4 errors=remount-ro 0 1
# /boot/efi was on /dev/sda1 during installation
UUID=B91C-053A /boot/efi vfat umask=0077 0 1
/swapfile
# 后向引用前边加#号即可
:%s/^\(UUID\)/#\1/g
# 第二种写法,直接替换
:%s/^UUID/#UUID/g
结果如下:
yang@yang-user:~/tmp$ cat fstab
# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# <file system> <mount point> <type> <options> <dump> <pass>
# / was on /dev/sda2 during installation
#UUID=0c6a8991-acef-4f2c-83bc-72f74b7bce4d / ext4 errors=remount-ro 0 1
# /boot/efi was on /dev/sda1 during installation
#UUID=B91C-053A /boot/efi vfat umask=0077 0 1
/swapfile
4.2、或者
| 或者
a|b #a或b
C|cat #C或cat
(C|c)at #Cat或cat
例1:去掉空行和注释行
yang@yang-user:~$ grep -v "^#" ./tmp/fstab |grep -v "^$"
UUID=0c6a8991-acef-4f2c-83bc-72f74b7bce4d / ext4 errors=remount-ro 0 1
UUID=B91C-053A /boot/efi vfat umask=0077 0 1
/swapfile none swap sw 0 0
# 用或者进行去除
yang@yang-user:~$ grep -v "^#\|^$" ./tmp/fstab
UUID=0c6a8991-acef-4f2c-83bc-72f74b7bce4d / ext4 errors=remount-ro 0 1
UUID=B91C-053A /boot/efi vfat umask=0077 0 1
/swapfile none swap sw 0 0
# 都有"^",可以合并
yang@yang-user:~$ grep -v "^\(#\|$\)" ./tmp/fstab
UUID=0c6a8991-acef-4f2c-83bc-72f74b7bce4d / ext4 errors=remount-ro 0 1
UUID=B91C-053A /boot/efi vfat umask=0077 0 1
/swapfile none swap sw 0 0
# 其他写法,[]代表任意的一个字符,空行没有字符,^字符放到[]里就代表排除的意思,放到[]外边代表以什么开头的行,
# ^[^#]表示不是以#号开头的,不以#号开头的任意字符
yang@yang-user:~$ grep "^[^#]" ./tmp/fstab
UUID=0c6a8991-acef-4f2c-83bc-72f74b7bce4d / ext4 errors=remount-ro 0 1
UUID=B91C-053A /boot/efi vfat umask=0077 0 1
/swapfile none swap sw 0 0
5、扩展正则表达式
# grep 使用-E参数为扩展正则表达式,可以去掉“\”
ang@yang-user:~$ grep -Ev "^(#|$)" ./tmp/fstab
UUID=0c6a8991-acef-4f2c-83bc-72f74b7bce4d / ext4 errors=remount-ro 0 1
UUID=B91C-053A /boot/efi vfat umask=0077 0 1
/swapfile none swap sw 0 0
# egrep = grep -E
yang@yang-user:~$ egrep -v "^(#|$)" ./tmp/fstab
UUID=0c6a8991-acef-4f2c-83bc-72f74b7bce4d / ext4 errors=remount-ro 0 1
UUID=B91C-053A /boot/efi vfat umask=0077 0 1
/swapfile none swap sw 0 0
例1:取出IP地址
# 查看IP地址
yang@yang-user:~$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: enp3s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether b0:83:fe:82:df:4f brd ff:ff:ff:ff:ff:ff
inet 192.168.31.206/24 brd 192.168.31.255 scope global dynamic noprefixroute enp3s0
valid_lft 29889sec preferred_lft 29889sec
inet6 fe80::9460:76bf:c8c0:57d8/64 scope link noprefixroute
valid_lft forever preferred_lft forever
取出符和要求的IP地址段
# IP地址一共是4段,每段之间用“.”隔开,每段最少是1位,最多是3位,[0-9]表示:0-9任意一个数字,{1,3}表示最少是位,最多是3位
# [0-9]{1,3}\.最后的“.”在正则表达式中有含义的,在正则表带是里“.”表示一个字符,这里边“.”就是本身,所以需要加“\”进行转义
yang@yang-user:~$ ip a |grep -E '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'
inet 127.0.0.1/8 scope host lo
inet 192.168.31.206/24 brd 192.168.31.255 scope global dynamic noprefixroute enp3s0
去除多余部分
# 加上-o 参数,把符合要求的打印出来,每匹配一个就打印一个
yang@yang-user:~$ ip a |grep -Eo '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'
127.0.0.1
192.168.31.206
192.168.31.255
取出正确的IP地址
# 去掉回环地址
yang@yang-user:~$ ip a |grep -Eo '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' |grep -v ^127
192.168.31.206
192.168.31.255
# 取出正确的IP地址(第一个)
yang@yang-user:~$ ip a |grep -Eo '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' |grep -v ^127 |head -1
192.168.31.206
更加简洁的写法
# [0-9]{1,3}\.整体出现了三次,前边作为一个整体,{3}表示出现三次,再加上最后一次即可
yang@yang-user:~$ ip a |grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' |grep -v ^127 |head -1
192.168.31.206
其他写法
# 将常用的正则表达式写成一个问题,下次用到直接用-f参数调用
yang@yang-user:~$ vim regex.txt
([0-9]{1,3}\.){3}.[0-9]{1,3}
# 调用写好的正则表达式文件
yang@yang-user:~$ ip a |grep -Eof regex.txt |head -1
192.168.31.206
注意: [[ ]] 和单[ ] 的区别就是如果使用正则表达式就双中括号,如果不用正则表达式就用单中括号。