基础正则表达式

 

 

 

 

 

 

 

基本正则表达式

 

 

 

(一)正则表达式介绍

 

 

正则表达式是处理文件的内容,也就是字符

 

REGEXP :由一类特殊字符及文本字符所编写的模式,其中有些字符(元字符)不表示字符字面意义,而表示控制或通配的功能。


程序支持: :grep,sed,awk,vim, less,nginx,varnish等


分两类:


基本正则表达式:BRE


扩展正则表达式:ERE。grep -E, egrep


正则表达式引擎:


采用不同算法,检查处理正则表达式的软件模块PCRE(Perl Compatible Regular Expressions)


元字符分类:字符匹配、匹配次数、位置锚定、分组

 

 

文件名可以使用通配符表示

*是通配符,涉及到文件管理。通配符是模糊匹配

aaa  aa.txt  access_log  anaconda-ks.cfg  anan.diff
[root@centos72 ~]# ls  a*  -l
-rw-r--r--. 1 root root        9 May 7 13:28 aaa -rw-r--r--. 1 root root 27 May 7 19:11 aa.txt -rw-r--r--. 1 root root 14372536 May 7 22:31 access_log -rw-------. 1 root root 1592 Jan 13 00:22 anaconda-ks.cfg -rw-r--r--. 1 root root 359 May 7 23:07 anan.diff

 

 

 

 

正则表达式是匹配字符串,不是匹配文件名。正则表达式是通用技术,对于开发也适用,所以非常重要。

 

学会了基本正则表达式也就学会了扩展的正则表达式。

 

正则表达式涉及到算法,正则表达式引擎和汽车的发动机类似,实际上就是软件。


采用不同算法,检查处理正则表达式的软件模块PCRE(Perl Compatible Regular Expressions)

 

perl语言功能太强太灵活,这就让很多人很难掌握。

 

代码主要是维护,经常要进行修改,所以代码写的容易理解是最好的

 

diff工具促进了开源软件的发展,因为可以对比代码

[root@centos72 ~]# rpm  -q  pcre
pcre-8.32-17.el7.x86_64
[root@centos72 ~]# which pcre /usr/bin/which: no pcre in (/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin) [root@centos72 ~]# yum whatprovides pcre Loaded plugins: fastestmirror Loading mirror speeds from cached hostfile pcre-8.32-17.el7.x86_64 : Perl-compatible regular expression library Repo : base pcre-8.32-17.el7.x86_64 : Perl-compatible regular expression library Repo : @anaconda

 

 

 

 

 

查看帮助

Regular  expressions  ("RE"s),  as  defined in POSIX.2表示其为国际上开发软件的标准

[root@centos72 ~]# man  7 regex
No manual entry for regex in section 7 [root@centos72 ~]# yum install man-pages Loaded plugins: fastestmirror Loading mirror speeds from cached hostfile Resolving Dependencies --> Running transaction check ---> Package man-pages.noarch 0:3.53-5.el7 will be installed --> Finished Dependency Resolution Dependencies Resolved ======================================================================================================== Package Arch Version Repository Size ======================================================================================================== Installing: man-pages noarch 3.53-5.el7 base 5.0 M Transaction Summary ======================================================================================================== Install 1 Package Total download size: 5.0 M Installed size: 4.6 M Is this ok [y/d/N]: y Downloading packages: Running transaction check Running transaction test Transaction test succeeded Running transaction Installing : man-pages-3.53-5.el7.noarch 1/1 Verifying : man-pages-3.53-5.el7.noarch 1/1 Installed: man-pages.noarch 0:3.53-5.el7 Complete! [root@centos72 ~]# man 7 regex

 

 

 

 

 

 

(二)基本正则表达式元字符匹配

 

. 匹配任意单个字符


[ ] 匹配指定范围内的任意单个字符


[ ^ ]  匹配指定范围外的任意单个字符


[:alnum:]  字母和数字


[:alpha:]  代表任何英文大小写字符,亦即 A-Z, a-z


[:lower:]  小写字母 [:upper:]  大写字母


[:blank:]  空白字符(空格和制表符)


[:space:]  水平和垂直的空白字符(比[:blank:] 包含的范围广)


[:cntrl:]  不可打印的控制字符(退格、删除、警铃...)
[:digit:] 字 十进制数字 [:xdigit:] 十六进制数字


[:graph:]  可打印的非空白字符


[:print:]  可打印字符


[:punct:] 标点符号

 

 

 

 

 

(1). 匹配任意单个字符

[root@centos72 ~]# grep  r..t  /etc/passwd
root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin [root@centos72 ~]# grep root /etc/passwd root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin

 

 

 

 

 

 

(2)[ ] 匹配指定范围内的任意单个字符

和文件通配符类似,只是适用的地方不同

[root@centos72 ~]# grep  [abco][abo]  /etc/passwd
root:x:0:0:root:/root:/bin/bash lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin mail:x:8:12:mail:/var/spool/mail:/sbin/nologin operator:x:11:0:operator:/root:/sbin/nologin nobody:x:99:99:Nobody:/:/sbin/nologin postfix:x:89:89::/var/spool/postfix:/sbin/nologin wang:x:1000:1000:wang:/home/wang:/bin/bash [root@centos72 ~]# grep r[abco][abo]t /etc/passwd root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin 

 

 

 

 

 

[root@centos72 ~]# echo   raat   |  grep  r[abco][abo]t 
raat
[root@centos72 ~]# echo   ract   |  grep r[abco][abo]t [root@centos72 ~]# echo rabt | grep r[abco][abo]t rabt [root@centos72 ~]# echo raot | grep r[abco][abo]t raot

 

 

 

 

 

 

 

(3)[^]  匹配指定范围外的任意单个字符

[root@centos72 ~]# echo   ract   |  grep  r[abco][^abo]t 
ract
[root@centos72 ~]# echo   ract   |  grep  r[^abco][^abo]t 

 

 

 

 

 

 

(4)[:digit:] 字 十进制数字 [:xdigit:] 十六进制数字

取出主版本号

在写脚本的时候要判断版本的不同

[[:digit:]]注意要写两个中括号,第1个中括号表示0-9,第2个中括号表示0-9的某个数字

[root@centos72 ~]# cat  /etc/centos-release  |  grep   [[:digit:]]
CentOS Linux release 7.5.1804 (Core) 

 

 

 

 

 

 

 

只取6和7这个数字

如果版本在达到了10,那么也要对其考虑

[root@centos72 ~]# cat  /etc/centos-release  |  grep   -o  [[:digit:]]  
7
5
1 8 0 4

 

 

 

 

使用head过滤出了第1个

[root@centos72 ~]# cat  /etc/centos-release  |  grep   -o  [[:digit:]]  | head -n1
7

 

 

 

 

 

 取出6版本

[root@centos65 ~]# cat  /etc/centos-release  |  grep   -o  [[:digit:]]  
6
8

 

 

 

 

[root@centos65 ~]# cat  /etc/centos-release  |  grep   -o  [[:digit:]]  | head -n1
6

 

 

 

 

 

 如果版本在达到了10,那么也要对其考虑

[root@centos72 ~]# cat  /app/centos-release 
CentOS Linux release 17.5.1804 (Core) 
[root@centos72 ~]# cat /app/centos-release | grep -o [[:digit:]] 1 7 5 1 8 0 4 [root@centos72 ~]# cat /app/centos-release | grep -w [[:digit:]] CentOS Linux release 17.5.1804 (Core) 

 

 

 

 

 

 

 

(三)匹配次数

 

匹配次数:用在要指定次数的字符后面,用于指定前面的字符要出现的次数


*  匹配 前面的字符任意次,包括0次


贪婪模式:尽可能长的匹配


.*  任意 长度的任意字符


\?  匹配 其前面的字符0 或1次


\+  匹配 其前面的字符至少1次


\{n\}  匹配 前面的字符n次


\{m,n\}  匹配 前面的字符至少m 次,至多n次


\{,n\}  匹配 前面的字符至多n次


\{n,\}  匹配 前面的字符至少n次

 

 

 

ab*的情况是a,ab,abb,abbb,abbbb.......

所以和a没有关系的,只是打酱油的

 

 

 

 

文件通配符的*和正则表达式的*不一样。

通配符针对的是文件,比如下面匹配的是a开头的文件

[root@centos72 ~]# ls  a*
aaa  aa.txt  access_log  anaconda-ks.cfg  anan.diff
[root@centos72 ~]# ll  a*  
-rw-r--r--. 1 root root        9 May 7 13:28 aaa -rw-r--r--. 1 root root 27 May 7 19:11 aa.txt -rw-r--r--. 1 root root 14372536 May 7 22:31 access_log -rw-------. 1 root root 1592 Jan 13 00:22 anaconda-ks.cfg -rw-r--r--. 1 root root 359 May 7 23:07 anan.diff

 

 

 

 

 

[root@centos72 ~]# grep  root  /etc/passwd
root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin [root@centos72 ~]# grep ro*t /etc/passwd root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin

 

 

 

 

 

(2).*  任意长度的任意字符

因为.是匹配任意单个字符,*匹配前面的字符任意次

贪婪模式:尽可能长的匹配,经常使用

巧记吃了点心才有动力去做任何事情

[root@centos72 ~]# grep   r.*t  /etc/passwd
root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin polkitd:x:999:998:User for polkitd:/:/sbin/nologin sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin postfix:x:89:89::/var/spool/postfix:/sbin/nologin apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin 

 

 

 

 

[root@centos72 ~]# ls  |  grep  a.*
aaa
aa.txt
access_log
anaconda-ks.cfg

 

 

 

 

 

注意如果涉及到文件名,那么关键字就要加双引号,否则会被认为是通配符

[root@centos72 ~]# grep  "a*"  anaconda-ks.cfg 
#version=DEVEL # System authorization information auth --enableshadow --passalgo=sha512 # Use CDROM installation media cdrom # Use graphical install graphical # Run the Setup Agent on first boot firstboot --enable ignoredisk --only-use=sda # Keyboard layouts keyboard --vckeymap=us --xlayouts='us' # System language lang en_US.UTF-8 # Network information network --bootproto=dhcp --device=ens33 --onboot=off --ipv6=auto --no-activate network --hostname=centos72.huawei.com # Root password rootpw --iscrypted $6$3ZpKJEd3ctkruWkF$ACv/Y4HSNb4lTqk4Gbol157B2lHw0AVcKM1rjEshEOrMcIIXw1DvoPPCZy3y3i.SijcTdTAfvFs/uFPwLxKd51 # System services services --disabled="chronyd" # System timezone timezone Asia/Shanghai --isUtc --nontp user --name=wang --password=$6$PqqaCIq7qipkXclF$5idE9A8TzG/yLzqHbmlSg9cVaNUmxPG85y/K81a0KSrosFH/srLzY0HQxeTUMZKs.KVoyJOphaA8Xz.nidUF// --iscrypted --gecos="wang" # System bootloader configuration bootloader --location=mbr --boot-drive=sda # Partition clearing information clearpart --none --initlabel # Disk partitioning information part swap --fstype="swap" --ondisk=sda --size=2048 part /app --fstype="xfs" --ondisk=sda --size=20480 part / --fstype="xfs" --ondisk=sda --size=51200 part /boot --fstype="xfs" --ondisk=sda --size=1024 %packages @^minimal @core %end %addon com_redhat_kdump --disable --reserve-mb='auto' %end %anaconda pwpolicy root --minlen=6 --minquality=1 --notstrict --nochanges --notempty pwpolicy user --minlen=6 --minquality=1 --notstrict --nochanges --emptyok pwpolicy luks --minlen=6 --minquality=1 --notstrict --nochanges --notempty %end

 

 

 

 

 

[root@centos65 ~]# cat  anaconda-ks.cfg  |  grep  a*
[root@centos65 ~]# cat  anaconda-ks.cfg  |  grep  a* [root@centos65 ~]# cat anaconda-ks.cfg | grep a* [root@centos65 ~]# cat anaconda-ks.cfg | grep a* [root@centos65 ~]# cat anaconda-ks.cfg | grep a*

 

 

 

 

[root@centos72 ~]# grep  a* anaconda-ks.cfg 
[root@centos72 ~]# grep  a* anaconda-ks.cfg 
[root@centos72 ~]# grep  a* anaconda-ks.cfg [root@centos72 ~]# grep a* anaconda-ks.cfg [root@centos72 ~]# ls anaconda-ks.cfg | grep a* [root@centos72 ~]# ls anaconda-ks.cfg | grep a* [root@centos72 ~]# ls anaconda-ks.cfg | grep a* [root@centos72 ~]# ls anaconda-ks.cfg | grep "a*" anaconda-ks.cfg

 

 

 

 

 

 

通过管道传输之后,文件名就是字符,而添加双引号就是正则表达式

因为查看的文件名内容,所以后面的正则表达式要添加引号

[root@centos72 ~]# ls  |  grep  "a*" aaa aa.txt access_log anaconda-ks.cfg f1 f2 f3 f4 f5 grep

 

 

 

 

 

 

单引号也可以

[root@centos72 ~]# ls  |  grep  'a*' aaa aa.txt access_log anaconda-ks.cfg f1 f2 f3 f4 f5 grep

 

 

 

 

 

\?  匹配 其前面的字符0或1次,也就是前面的字符是可有可无的

因为查看的文件名内容,所以后面的正则表达式要添加引号

前面加上反斜线是因为在通配符里面?是有特殊含义的

[root@centos72 ~]# cat  /etc/passwd  |  grep  "ba\?" root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin sync:x:5:0:sync:/sbin:/bin/sync shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown halt:x:7:0:halt:/sbin:/sbin/halt mail:x:8:12:mail:/var/spool/mail:/sbin/nologin operator:x:11:0:operator:/root:/sbin/nologin games:x:12:100:games:/usr/games:/sbin/nologin ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin nobody:x:99:99:Nobody:/:/sbin/nologin systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin dbus:x:81:81:System message bus:/:/sbin/nologin polkitd:x:999:998:User for polkitd:/:/sbin/nologin sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin postfix:x:89:89::/var/spool/postfix:/sbin/nologin wang:x:1000:1000:wang:/home/wang:/bin/bash apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin

 

 

 

 

 

 

 

 

 

 

 

[root@centos65 ~]# cat  anaconda-ks.cfg  |  grep  a\?
[root@centos65 ~]# cat  anaconda-ks.cfg  |  grep  "a\?" # Kickstart file automatically generated by anaconda. #version=DEVEL install cdrom lang en_US.UTF-8 keyboard us network --onboot no --device eth0 --bootproto dhcp --noipv6 rootpw --iscrypted $6$PnGoqdV0v.gilohW$pJsYiUbd8ZRFVyVXnZzJQutfCR.WGGsJGREUV4r6IguF9mBPXog/UJVw7RBdnF4m76RuGaQHHBZiAv46LcugO1 firewall --service=ssh authconfig --enableshadow --passalgo=sha512 selinux --enforcing timezone Asia/Shanghai bootloader --location=mbr --driveorder=sda --append="crashkernel=auto rhgb quiet" # The following is the partition information you requested # Note that any partitions you deleted are not expressed # here so unless you clear all partitions first, this is # not guaranteed to work #clearpart --none #part /boot --fstype=ext4 --size=1024 #part / --fstype=ext4 --size=50000 #part /app --fstype=ext4 --size=20000 #part swap --size=2048 repo --name="CentOS" --baseurl=cdrom:sr0 --cost=100 %packages @core @server-policy @workstation-policy %end

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

全部都匹配了,0次或者1次

[root@centos72 ~]# cat  1.txt  |  grep  a\?
[root@centos72 ~]# cat  1.txt | grep "a\?" a ab 1 2 3 b c [root@centos72 ~]# cat 1.txt a ab 1 2 3 b c

 

 

 

 

 

 

 

 

 

 

 

 

注意是贪婪匹配,也就是只要包含就可以了,超过一次相同的关键字都可以

实际上也就是匹配了多次

比如"a\?"只要有一个a就可以,如果是aa,aaa也可以,匹配了2次,3次

[root@centos72 ~]# cat  2.txt  |  grep  "a\?" a aa aaa aaaa aaaaa 1234 123456 12a sdfg dfgaa dgbaaafv dgvhaaaaa [root@centos72 ~]# cat 2.txt | grep "a\?" | wc 12 12 66 [root@centos72 ~]# cat 2.txt a aa aaa aaaa aaaaa 1234 123456 12a sdfg dfgaa dgbaaafv dgvhaaaaa [root@centos72 ~]# cat 2.txt | wc 12 12 66

 

 

 

 

 

 匹配的关键字就显示红色

 

 

 

 

 

 

[root@centos72 ~]# cat  3.txt  |  grep  "ba\?" ba b bbb badgkcg fkfbljoajf baakkfj baaajko [root@centos72 ~]# cat 3.txt | grep "ba\?" | wc 7 7 44

 

 

 

 

[root@centos72 ~]# cat  3.txt 
ba
b
bbb
badgkcg
aaajlnhl
fkfbljoajf
baakkfj
baaajko
[root@centos72 ~]# cat  3.txt |  wc 8 8 53

 

 

 

 

 

 

 

 

 

 

 

 

 

 \+  匹配其前面的字符至少1次

[root@centos72 ~]# cat  /etc/passwd  |  grep  "ba\+" root:x:0:0:root:/root:/bin/bash wang:x:1000:1000:wang:/home/wang:/bin/bash

 

 

 

 

 

 

 

 

 

[root@centos72 ~]# cat  3.txt  |  grep  "ba\+" ba badgkcg baakkfj baaajko [root@centos72 ~]# cat 3.txt ba b bbb badgkcg aaajlnhl fkfbljoajf baakkfj baaajko

 

 

 

 

 

 

 

 

 

 

 

 

 \{n\}  匹配前面的字符n次

 先写大括号,中间是次数,在括号的前面都添加\,对大括号进行转义,最前面写上要过滤出来的关键字

下面表示匹配出现3次b

[root@centos72 ~]#  cat  3.txt  |  grep  "b\{3\}" bbb [root@centos72 ~]# cat 3.txt ba b bbb badgkcg aaajlnhl fkfbljoajf baakkfj baaajko 

 

 

 

 

[root@centos72 ~]# cat  3.txt 
ba
b
bbb
badgkcg
aaajlnhl
fkfbljoajf
baakkfj
baaajko

 

 

 

 

 

贪婪匹配

文件出现了2次及以上,那么2次都可匹配

[root@centos72 ~]#  cat  3.txt  |  grep  "b\{2\}" bbb [root@centos72 ~]# cat 3.txt | grep "b\{1\}" ba b bbb badgkcg fkfbljoajf baakkfj baaajko

 

 

 

 

 

 

 

 

 

 

 

 

 

 

正则表达式最好都添加引号

[root@centos72 ~]# echo  "bbb"  |   grep  "b\{1\}" bbb [root@centos72 ~]# echo "bbb" | grep "b\{1\}" bbb [root@centos72 ~]# echo 'bbb' | grep "b\{1\}" bbb [root@centos72 ~]# echo 'bbb' | grep "b\{2\}" bbb [root@centos72 ~]# echo 'bbb' | grep "b\{3\}" bbb [root@centos72 ~]# echo 'bbb' | grep b\{3\} [root@centos72 ~]# echo 'bbb' | grep b\{3\} [root@centos72 ~]# echo 'bbb' | grep b\{2\} [root@centos72 ~]# echo 'bbb' | grep b\{1\} [root@centos72 ~]# echo "bbb" | grep b\{1\} [root@centos72 ~]# echo "bbb" | grep b\{2\} [root@centos72 ~]# echo "bbb" | grep b\{2\} 

 

 

 

 

 单引号也可以

[root@centos72 ~]# cat  /etc/passwd  | grep  'o\{1\}' root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown mail:x:8:12:mail:/var/spool/mail:/sbin/nologin operator:x:11:0:operator:/root:/sbin/nologin games:x:12:100:games:/usr/games:/sbin/nologin ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin nobody:x:99:99:Nobody:/:/sbin/nologin systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin dbus:x:81:81:System message bus:/:/sbin/nologin polkitd:x:999:998:User for polkitd:/:/sbin/nologin sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin postfix:x:89:89::/var/spool/postfix:/sbin/nologin wang:x:1000:1000:wang:/home/wang:/bin/bash 

 

 

 

 

 

 

 

 

 

 

\{m,n\}  匹配 前面的字符至少m次,至多n次

[root@centos72 ~]# echo  "bbb"  |   grep  "b\{1,3\}" bbb [root@centos72 ~]# echo "bbb" | grep "b\{1,2\}" bbb

 

 

 

 

[root@centos72 ~]# cat  /etc/fstab    |   grep  "u\{2,3\}" [root@centos72 ~]# cat /etc/fstab | grep -i "u\{2,3\}" UUID=5998ead0-b370-4859-9153-ecf4e2b9dd84 / xfs defaults 0 0 UUID=ac6bb7e3-fa78-4eb2-b00d-e85c421c1bb0 /app xfs defaults 0 0 UUID=92886c3f-42a3-40f4-8cf7-c6890ca3a52e /boot xfs defaults 0 0 UUID=104520e1-0e97-4248-8fd0-a21e7d88a881 swap swap defaults 0 0

 

 

 

 

 

 

 

 

 

[root@centos72 ~]# cat  /etc/services | grep  "u\{2,3\}" uucp-path 117/tcp uucp-path 117/udp uucp 540/tcp uucpd # uucp daemon uucp 540/udp # uucpd uucp-rlogin 541/tcp # uucp-rlogin uucp-rlogin 541/udp # uucp-rlogin uuidgen 697/tcp # UUIDGEN uuidgen 697/udp # UUIDGEN opequus-server 2400/tcp # OpEquus Server opequus-server 2400/udp # OpEquus Server suucp 4031/tcp # UUCP over SSL suucp 4031/udp # UUCP over SSL continuus 5412/tcp # Continuus continuus 5412/udp # Continuus aequus 23456/tcp # Aequus Service aequus-alt 23457/tcp # Aequus Service Mgmt [root@centos72 ~]# 

 

 

 

 

 

 

 

[root@centos72 ~]# echo  "bbbbbbbbbb"  |   grep  "b\{5,9\}" bbbbbbbbbb [root@centos72 ~]# echo "bbbbbbbbb" | grep "b\{5,9\}" bbbbbbbbb [root@centos72 ~]# echo "bbbbbbbb" | grep "b\{5,9\}" bbbbbbbb [root@centos72 ~]# echo "bbbbbbb" | grep "b\{5,9\}" bbbbbbb [root@centos72 ~]# echo "bbbbbb" | grep "b\{5,9\}" bbbbbb [root@centos72 ~]# echo "bbbbbbbbbb" | wc 1 1 11

 

 

 

 

 

 

 

 

 

 

 

 

 

如果是最小次数或者最大次数的整数倍,那么就会匹配整数次

刚好30个字符,那么匹配了6次,也就是最大次数的整数倍

[root@centos72 ~]# echo  "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"  |   grep  "b\{4,5\}" bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb [root@centos72 ~]# echo "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" | wc 1 1 31

 

 

 

 

 

 

 

 

刚好14个字符,那么匹配了7次,也就是最小次数的整数倍

[root@centos72 ~]#  echo  "bbbbbbbbbbbbbb" | grep "b\{2,4\}" bbbbbbbbbbbbbb [root@centos72 ~]# echo "bbbbbbbbbbbbbb" | wc 1 1 15

 

 

 

 

 

 

 

 

 

 

 

 

也可以匹配最小次数和最大次数的整数倍

3和7加起来是10,刚好匹配了1次最小次数和最大次数之和

[root@centos72 ~]#  echo  "bbbbbbbbbb" | wc
      1 1 11 [root@centos72 ~]# echo "bbbbbbbbbb" | grep "b\{3,7\}" bbbbbbbbbb

 

 

 

 

 

 

 

 

 

 

 

注意不可以匹配最小次数和最大次数之间的次数,比如最小次数和最大次数是3,5

那么不可以匹配到7

 

 

 

 

 

 

 

 

 

\{,n\}  匹配 前面的字符至多n次

虽然是7个字符,超过了5次,但是可以匹配多次

[root@centos72 ~]# echo  "bbbbbbb" |   wc
      1 1 8 [root@centos72 ~]# echo "bbbbbbb" | grep "b\{3,5\}" bbbbbbb [root@centos72 ~]# echo "bbbbbbb" | grep "b\{,5\}" bbbbbbb

 

 

 

 

 

 

 

 

 

 

\{n,\}  匹配 前面的字符至少n次

[root@centos72 ~]# echo  "bbbbbbb" |   wc
      1 1 8 [root@centos72 ~]# echo "bbbbbbb" | grep "b\{3,5\}" bbbbbbb [root@centos72 ~]# echo "bbbbbbb" | grep "b\{,5\}" bbbbbbb [root@centos72 ~]# echo "bbbbbbb" | grep "b\{5,\}" bbbbbbb

 

 

 

 

 

 

 

 

 

 为什么显示的是8个字符,实际上是7个字符,因为回车换行占用一个字符

[root@centos72 ~]# cat  4.txt 
bbbbbbb
[root@centos72 ~]# cat  4.txt  | wc 1 1 8 [root@centos72 ~]# hexdump -C 4.txt 00000000 62 62 62 62 62 62 62 0a |bbbbbbb.| 00000008

 

 

 

 

 

 

[root@centos72 ~]# cat  5.txt 
aaa
aaaa
aaaaa
aaaaaa
bcdaaaa
hdkfaaaa
hkflieaaaajdlhw
aa
a
fgbaa
[root@centos72 ~]# cat  5.txt  |  grep a\{3,\} [root@centos72 ~]# cat 5.txt | grep "a\{3,\}" aaa aaaa aaaaa aaaaaa bcdaaaa hdkfaaaa hkflieaaaajdlhw

 

 

 

 

 

 

 

 

 

 

 前面的a一定要有,后面的a可有可无

 

 

 

 

 

 

 

 

 

 

 

 

示例1——取出主版本号

 

 

适用一位数和两位数

目前6是主流,也发行centos7版本,如果8出来那么主流就是7了,一般是滞后一个版本

 

法1

-o: 仅显示匹配到的字符串

\+  匹配 其前面的字符至少1次

下面假设版本是17

[root@centos72 ~]# grep  -o  "[0-9]\+"   /app/centos-release
17 5 1804 [root@centos72 ~]# grep -o "[0-9]\+" /app/centos-release | head -n1 17

 

 

 

 

[root@centos65 ~]# echo  111  |   grep  -o  "[0-9]\+" 111 [root@centos65 ~]# echo "111" | grep -o "[0-9]\+" 111 [root@centos65 ~]# echo "123456" | grep -o "[0-9]\+" 123456 [root@centos65 ~]# echo 123456 | grep -o "[0-9]\+" 123456

 

 

 

 

 

 

[root@centos65 ~]# cp  /etc/centos-release   /app/
[root@centos65 ~]# vim  /app/centos-release 
[root@centos65 ~]# cat  /app/centos-release 
CentOS release 16.8 (Final) [root@centos65 ~]# grep -o "[0-9]\+" /app/centos-release | head -n1 16

 

 

 

[root@centos72 ~]# grep  -o  "[0-9]\+"  /etc/centos-release  | head  1 head: cannot open ‘1’ for reading: No such file or directory [root@centos72 ~]# grep -o "[0-9]\+" /etc/centos-release | head -n1 7 [root@centos72 ~]# grep -o "[0-9]\+" /etc/centos-release | head -1 7

 

 

 

 

 

 

 

现在的版本

[root@centos65 ~]# grep  -o  "[0-9]\+"  /etc/centos-release  | head  -n1 6 [root@centos65 ~]# grep -o "[0-9]\+" /etc/centos-release | head -1 6 [root@centos65 ~]# grep -o "[0-9]\+" /etc/centos-release | head 1 head: cannot open `1' for reading: No such file or directory

 

 

 

 

[root@centos65 ~]# cat   /etc/centos-release 
CentOS release 6.8 (Final)
[root@centos72 ~]# cat   /etc/centos-release CentOS Linux release 7.5.1804 (Core) 

 

 

 

 

 

 

 

 

 

法2:

去掉多余的空格

[root@centos72 ~]# cat  /app/centos-release  | tr -s ''
CentOS Linux release 17.5.1804 (Core) 

 

 

 

以空格为分割符取第4个字段

[root@centos72 ~]# cat  /app/centos-release  | tr -s '' | cut -d" " -f4 17.5.1804

 

 

 

 

 以点作为分割符


[root@centos72 ~]# cat  /app/centos-release  | tr -s '' | cut -d" " -f4 | grep "[[:digit:]]\{2\}" 17.5.1804 [root@centos72 ~]# cat /app/centos-release | tr -s '' | cut -d" " -f4 | grep "[[:digit:]]\{2\}" | cut -d. -f1 17

 

 

 

 

 

[root@centos65 ~]# cat  /app/centos-release |tr -s '' | cut -d" " -f3 | grep "[[:digit:]]\{1\}" |cut -d. -f1 16

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

(四)位置锚定

 

位置锚定:定位出现的位置
^  行首锚定,用于模式的最左侧
$  行尾锚定,用于模式的最右侧
^PATTERN$ 用于模式匹配整行
^$ 空行
^[[:space:]]*$ 空白行
\< 或 或 \b  词首锚定,用于单词模式的左侧
\> 或 或 \b  词尾锚定;用于单词模式的右侧
\<PATTERN\>

 

 

 

 

单纯的过滤不管关键字在什么地方都会显示

[root@centos72 ~]# grep  root  /etc/passwd
root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin

 

 

 

 

 

 

 

 

(1) ^ 行首锚定,用于模式的最左侧

 显示以某个关键字开头的行

[root@centos72 ~]# grep  ^root  /etc/passwd
root:x:0:0:root:/root:/bin/bash [root@centos72 ~]# grep "^root" /etc/passwd root:x:0:0:root:/root:/bin/bash

 

 

 

[root@centos72 ~]# grep  '^root'  /etc/passwd root:x:0:0:root:/root:/bin/bash

 

 

 

 

 

 

 

 

 

 

(2)$行尾锚定,用于模式的最右侧


[root@centos72 ~]# grep  'bash$'  /etc/passwd root:x:0:0:root:/root:/bin/bash wang:x:1000:1000:wang:/home/wang:/bin/bash

 

 

 

 

 

 

 

 

 

(3)显示非空行

空行就是起始和结尾没有任何内容;空白行表示空行的一种,而且还要空格

^$ 空行

^[[:space:]]*$ 空白行

[root@centos72 ~]# cat  /etc/issue 
\S
Kernel \r on an \m

[root@centos72 ~]# cat  /etc/issue | grep  ^$

[root@centos72 ~]# cat /etc/issue | grep "^$" [root@centos72 ~]# cat /etc/issue | grep "^$" | wc 1 0 1

 

 

 

 

 

 

空行和空白行显示的内容是一样的

[root@centos72 ~]# cat /etc/issue  |  grep  ^[[:space:]]*$

[root@centos72 ~]# cat /etc/issue  |  grep  ^[[:space:]]*$ | wc 1 0 1

 

 

 

 

 

 

下面是空行,但空行没有空格

 

 

 

 

 

 

 

 

空格是肉眼看不出来的

 

[root@centos72 ~]# cat -A  /etc/issue
\S$
Kernel \r on an \m$
$

 

 

 

 

取反即可

 

[root@centos72 ~]# cat  /etc/issue | grep  -v   "^$" \S Kernel \r on an \m [root@centos72 ~]# cat /etc/issue | grep -v "^$" | wc 2 6 22

 

 

[root@centos72 ~]# cat /etc/issue  |  grep  -v ^[[:space:]]*$
\S
Kernel \r on an \m
[root@centos72 ~]# cat /etc/issue  |  grep  -v ^[[:space:]]*$  | wc 2 6 22

 

 

 

 

 

 

下面是空行,空行有空格

[root@centos72 ~]# cat -A  /etc/issue
\S$
Kernel \r on an \m$
       $

 

 

 

 

 

 

 

 

 

 

 

 

 (4)显示空白行

 "^$"只能显示空行,而^[[:space:]]*$的范围更广,可以显示空行和空白行

*表示0或者有

[root@centos72 ~]# cat  /etc/issue | grep  "^$" [root@centos72 ~]# cat /etc/issue | grep "^$" | wc 0 0 0 [root@centos72 ~]# cat /etc/issue | grep ^[[:space:]]*$ [root@centos72 ~]# cat /etc/issue | grep ^[[:space:]]*$ | wc 1 0 8

 

 

 

 

 

[root@centos72 ~]# cat /etc/issue  |  grep  -v ^$  | wc
      3       6      30 [root@centos72 ~]# cat /etc/issue | grep -v ^$ \S Kernel \r on an \m [root@centos72 ~]# cat /etc/issue | grep -v ^[[:space:]]*$ \S Kernel \r on an \m [root@centos72 ~]# cat /etc/issue | grep -v ^[[:space:]]*$ | wc 2 6 22

 

 

 

 

 

 

 "^[[:space:]]$"和"^$"显示的结果一样

"^[[:space:]]$"表示空格或者tab键,也就是有换行

而"^$"显示的是空行

[root@centos72 ~]# cat /etc/issue  |  grep  "^[[:space:]]$" [root@centos72 ~]# cat /etc/issue | grep "^[[:space:]]$" | wc 0 0 0

 

 

 

 

 

 

(5)\< 或  \b  词首锚定,用于单词模式的左侧

\<可以理解为倒下的脱字符^

左边小括号就是词首,因为我们习惯于从左到右看内容

注意要判断是否为单词,那么除了子母数字下划线的都是单词的分隔符

[root@centos72 ~]# cat  /etc/passwd  | grep    "\<r" root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin

 

 

 

 

 

 

 

 

 

 

[root@centos72 ~]# cat  /etc/issue  | grep    "\<K" Kernel \r on an \m [root@centos72 ~]# cat /etc/issue | grep "\<k" [root@centos72 ~]# cat /etc/issue | grep -i "\<k" Kernel \r on an \m

 

 

 

 

 

 

 

 

 

 

 

 

 注意要判断是否为单词,那么除了子母数字下划线的都是单词的分隔符

[root@centos72 ~]# echo  "aa_root"   | grep    "\<r" [root@centos72 ~]# echo "aa1root" | grep "\<r" [root@centos72 ~]# echo "aacroot" | grep "\<r" [root@centos72 ~]# echo "aa-root" | grep "\<r" aa-root [root@centos72 ~]# echo "aa root" | grep "\<r" aa root [root@centos72 ~]# echo "aa+ root" | grep "\<r" aa+ root [root@centos72 ~]# echo "aa=root" | grep "\<r" aa=root [root@centos72 ~]# echo "aa@root" | grep "\<r" aa@root [root@centos72 ~]# echo "aa!root" | grep "\<r" aa!root [root@centos72 ~]# echo "aa……root" | grep "\<r" aa……root

 

 

 

 

 

 

 \> 或 或 \b 词尾锚定;用于单词模式的右侧

[root@centos72 ~]# cat  /etc/passwd  | grep    "h\>" root:x:0:0:root:/root:/bin/bash wang:x:1000:1000:wang:/home/wang:/bin/bash

 

 

 

 

 

 

 

 

 

 

 

 注意不要使用b作为词首词尾锚定,容易搞混的

[root@centos72 ~]# echo  "aa……root"   | grep    "\br" aa……root [root@centos72 ~]# echo "aa……rootr" | grep "r\b" aa……rootr [root@centos72 ~]# echo "aa……rootr" | grep "\br\b"

 

 

 

 

 

 

 

 

 

 

 

(6)\<PATTERN\>  匹配整个单词,也就是完全匹配

[root@centos72 ~]# cat  /etc/passwd | grep  "\<root>\"
> ^C [root@centos72 ~]# cat /etc/passwd | grep "\<root\>" root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin

 

 

 

 

 

 

 

注意cat /etc/passwd | grep root是模糊匹配

[root@centos72 ~]# cat  /etc/passwd | grep  "\<root\>" root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin [root@centos72 ~]# cat /etc/passwd | grep root root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin [root@centos72 ~]# cat /etc/passwd | grep "\<root\>" | wc 2 2 77 [root@centos72 ~]# cat /etc/passwd | grep root | wc 2 2 77

 

 

 

 

 

 

 

 

 

 创建一个用户

注意cat /etc/passwd | grep root是模糊匹配,范围更广

[root@centos72 ~]# useradd  rooter
[root@centos72 ~]# cat  /etc/passwd | grep  root 
root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin rooter:x:1001:1001::/home/rooter:/bin/bash [root@centos72 ~]# cat /etc/passwd | grep "\<root\>" root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin [root@centos72 ~]# cat /etc/passwd | grep root | wc 3 3 120 [root@centos72 ~]# cat /etc/passwd | grep "\<root\>" | wc 2 2 77 [root@centos72 ~]# 

 

 

 

 

完全匹配和加上选项w的结果是一样的

[root@centos72 ~]# cat  /etc/passwd | grep -w   root 
root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin [root@centos72 ~]# cat /etc/passwd | grep -w root | wc 2 2 77

 

 

 

 

在下面的文件里面有很多带下划线的单纯后面加上()比如apply_sysctl()

[root@centos72 ~]# cat /etc/init.d/functions 

 

 

 

 

 

 

显示含有数字字母或者下划线的函数

[root@centos72 ~]# cat /etc/init.d/functions  | grep  ".*{$" systemctl_redirect () { checkpid() { __kill_pids_term_kill_checkpids() { __kill_pids_term_kill() { __pids_var_run() { __pids_pidof() { daemon() { killproc() { pidfileofproc() { pidofproc() { status() { echo_success() { echo_failure() { echo_passed() { echo_warning() { update_boot_stage() { success() { failure() { passed() { warning() { action() { strstr() { is_ignored_file() { convert2sec() { is_true() { is_false() { apply_sysctl() { [root@centos72 ~]# cat /etc/init.d/functions | grep ".*{$" | tr -d { systemctl_redirect () checkpid() __kill_pids_term_kill_checkpids() __kill_pids_term_kill() __pids_var_run() __pids_pidof() daemon() killproc() pidfileofproc() pidofproc() status() echo_success() echo_failure() echo_passed() echo_warning() update_boot_stage() success() failure() passed() warning() action() strstr() is_ignored_file() convert2sec() is_true() is_false() apply_sysctl() 

 

 

 

 

 

 

法2

_和a-Z是或的关系

[root@centos72 ~]# grep  -o   "^[_a-Z]*()"   /etc/init.d/functions 
checkpid()
__kill_pids_term_kill_checkpids()
__kill_pids_term_kill()
__pids_var_run()
__pids_pidof()
daemon()
killproc()
pidfileofproc()
pidofproc()
status()
echo_success()
echo_failure()
echo_passed()
echo_warning()
update_boot_stage()
success()
failure()
passed()
warning()
action()
strstr()
is_ignored_file()
is_true()
is_false()
apply_sysctl()
[root@centos72 ~]# 

 

 

 

 

 

 

法3

字母数字下划线是必须要有1个,空白可有可无

[root@centos72 ~]# grep  -o   "^[[:alnum:]_]\+[[:space:]]*()"   /etc/init.d/functions   
systemctl_redirect ()
checkpid()
__kill_pids_term_kill_checkpids()
__kill_pids_term_kill()
__pids_var_run()
__pids_pidof()
daemon()
killproc()
pidfileofproc()
pidofproc()
status()
echo_success()
echo_failure()
echo_passed()
echo_warning()
update_boot_stage()
success()
failure()
passed()
warning()
action()
strstr()
is_ignored_file()
convert2sec()
is_true()
is_false()
apply_sysctl()
[root@centos72 ~]# grep -o "^[[:alnum:]_]\+[[:space:]]*()" /etc/init.d/functions | wc 27 28 384 [root@centos72 ~]# 

 

 

 

 

 

(7)分组:\(\)  将一个或多个字符捆绑在一起,当作一个整体进行处理。


分组括号中的模式匹配到的内容会被正则表达式引擎记录于内部的变量中,

 

这些变量的命名方式为: \1, \2, \3, ...


\1 表示从左侧起第一个左括号以及与之匹配右括号之间的模式所匹配到的字符

 


示例: \(string1\+\(string2\)*\)
\1  :string1\+\(string2\)*
\2  :string2

 

 

 


(8)后向引用:引用前面的分组括号中的模式所匹配字符 , 而非模式本身
或者: :\|
示例:a\|b: a 或b

C\|cat: C 或cat

\(C\|c\)at:Cat 或cat

 

 

 

 

注意\是对(),|进行转义

下面\(a\|b\|c\)是一个整体,并且是或的关系

[root@centos72 ~]# echo  ax bx cx  | grep  "\(a\|b\|c\)x" ax bx cx

 

 

 

 

 

如果不加括号,那么就不是组合了

[root@centos72 ~]# echo  ax bx cx  | grep  "\a\|b\|c\x" ax bx cx

 

 

 

 

 

 

显示t重复2次及以上

[root@centos72 ~]# echo  rootrootroottt  | grep  "root\{2,\}" rootrootroottt

 

 

 

 

 

 

 

 

 

 

 

显示root重复2次及以上

注意在基本正则表达式里面\要写的,进行转义

[root@centos72 ~]# echo  rootrootroot  | grep  "\(root\)\{2,\}" rootrootroot

 

 

 

 

 

 

 

 

 

 

 分组:\(\)  将一个或多个字符捆绑在一起,当作一个整体进行处理

后向引用:引用前面的分组括号中的模式所匹配字符 , 而非模式本身

 

中间是两个字符都可以,空格不行

[root@centos72 ~]# echo  axyb  | grep  "\(a..b\)" axyb

 

 

 

[root@centos72 ~]# echo  ab  | grep  "\(a..b\)" [root@centos72 ~]# echo a b | grep "\(a..b\)" [root@centos72 ~]# echo a b | grep "\(a..b\)"

 

 

 

 

如果要表示axyb xx a12b yyy那么要使用两次分组

[root@centos72 ~]# echo  axyb  xx  a12b  yyy  | grep  "\(a..b\).*\(a..b\).*" axyb xx a12b yyy

 

 

 

 下面是3种不同的情况:

 

axyb xx  a12b yyy

axyb xx  axyb yyy

a12 xx  a12b yyy

 

 

使用正则表达式,可以使用相同的写法:

[root@centos72 ~]# echo  axyb  xx  a12b  yyy  | grep  "\(a..b\).*\(a..b\)*" axyb xx a12b yyy [root@centos72 ~]# echo axyb xx axyb yyy | grep "\(a..b\).*\(a..b\)*" axyb xx axyb yyy [root@centos72 ~]# echo a12b xx a12b yyy | grep "\(a..b\).*\(a..b\)*" a12b xx a12b yyy

 

 

 

[root@centos72 ~]# echo  axyb  xx  a12b  yyy  | grep  "\(a..b\).*\(a..b\).*" axyb xx a12b yyy [root@centos72 ~]# echo axyb xx axyb yyy | grep "\(a..b\).*\(a..b\).*" axyb xx axyb yyy [root@centos72 ~]# echo a12b xx a12b yyy | grep "\(a..b\).*\(a..b\).*" a12b xx a12b yyy

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

如果是后面两种情况,可以使用其他方法表示

因为出现了两次相同的字符,为了方便就不要再写一遍了

 \1代表了(a..b\)里面的表达出来的字符串

[root@centos72 ~]# echo  a12b  xx  a12b  yyy  | grep  "\(a..b\).*\1.*" a12b xx a12b yyy

 

 

 

 

 

 

 

 

 

 

 

 

 

 如果是两个正则表达式分组,并且是不同的

[root@centos72 ~]# echo  a12b  xx  n12m  yyy  | grep  "\(a..b\).*\(n..m\).*" a12b xx n12m yyy

 

 

 

 

\1调用了第1个分组(a..b\)里面的表达出来的字符串,.*都表示任意个任意字符串,\2表示调用了第2个分组(x..y \)里面的表达出来的字符串

后向引用:引用前面的分组括号中的模式所匹配字符 , 而非模式本身,在此例中就不是模式(a..b\)以及(x..y \),而是模式匹配出来的字符

\1对应第1个分组,\2对应第2个分组

0930(a..b\)出现两次并且是里面的字符是完全一样的,(x..y)出现两次并且是里面的字符是完全一样的

 

情况1

下面的完全一样的两部分:

[root@centos72 ~]# echo  a12bdggxxxxery a12bdggxxxxery |  grep   "\(a..b\).*\(x..y\).*\1.*\2" a12bdggxxxxery a12bdggxxxxery [root@centos72 ~]# echo a12bdggxxxxerya12bdggxxxxery | grep "\(a..b\).*\(x..y\).*\1.*\2" a12bdggxxxxerya12bdggxxxxery

 

 

 

 

 

 

 

 

 

 情况2

除了括号里面的都一样,其他的不一样

a12b和xery出现了两次

[root@centos72 ~]# echo  a12bdggxxxgdfdhfdsgxerya12bdggxxxfgdsgvntexery |  grep   "\(a..b\).*\(x..y\).*\1.*\2" a12bdggxxxgdfdhfdsgxerya12bdggxxxfgdsgvntexery

 

 

 

 

 

 

 

 

 情况3

没有出现两次a12b,虽然出现了两次xery,这是不能匹配的

[root@centos72 ~]# echo  a12bdggxxxgdfdhfdsgxerya34bdggxxxfgdsexery |  grep   "\(a..b\).*\(x..y\).*\1.*\2"

 

 

 

 

 \1 表示从左侧起第一个左括号以及与之匹配右括号之间的模式所匹配到的字符,

\2表示左侧起第2个左括号以及与之匹配右括号之间的模式所匹配到的字符

 \(string1\+\(string2\)*\)
\1  :string1\+\(string2\)*
\2  :string2

 

 

这里的\1分别代表了第2次出现的1234,xyz

搜索替代的时候很适合使用此技巧

[root@centos72 ~]# echo  12341234  | grep  "\(1..4\).*\1" 12341234 [root@centos72 ~]# echo xyzxyz | grep "\(x.z\).*\1" xyzxyz

 

 

 

 

 

 

 

 

 

 

或者: :\|

注意\表示对(),|进行转义


示例:a\|b: a 或b C\|cat: C 或cat \(C\|c\)at:Cat 或cat

 

转载于:https://www.cnblogs.com/wang618/p/11078720.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值