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

注意: [[ ]] 和单[ ] 的区别就是如果使用正则表达式就双中括号,如果不用正则表达式就用单中括号。