Linux正则表达式详解

2023年 12月 12日 52.5k 0

背景

    正则表达式(Regular Expression)的概念和发展可以追溯到20世纪50年代和60年代的计算机科学和形式语言理论的研究。正则表达式的发展涉及到多位科学家和计算机科学家的贡献。其中,美国计算机科学家和数学家Stephen Cole Kleene(斯蒂芬·科尔·克莱尼)被认为是正则表达式的先驱。他在20世纪50年代的研究中,提出了正则语言(Regular Language)的概念和形式化定义,为正则表达式的发展奠定了理论基础。他的研究成果在形式语言理论和计算机科学中有重要的影响。

    在正则表达式的实际应用和发展中,美国计算机科学家和Unix操作系统的创始人之一Ken Thompson(肯·汤普森)也发挥了重要的作用。在20世纪70年代,他在贝尔实验室开发的Unix操作系统中引入了正则表达式的概念和实现,将正则表达式作为一种强大的文本处理工具引入了计算机科学和软件开发的领域。

    简单来说,通过正则表达式,可以模糊匹配到满足条件的项,继而实现对该项的多维度操作。一般而言,Linux系统上经常使用到正则表达式的命令有vi、sed、grep、awk。

格式

    正则表达式在Linux实际使用时候,格式很简单,一般表示如下:

    /pattern/(sed/awk)或者 'pattern'(grep)

    其中,pattern(模式)代表需要进行匹配的内容,在Linux中,pattern分为普通正则以及扩展正则两种模式,在扩展正则下,新增/修改了部分pattern,常用的pattern请见下一章节。

   
 *注:如果需要使用扩展正则,在sed/grep中,请使用-E参数。但在awk中,默认就是扩展正则形式。

模式

    假如有一个需求,想要把a.txt中包括关键字test的行取出来,通常情况下有如下三种方式:

  1.     sed -n '/test/p' a.txt

  2.     grep 'test' a.txt

  3.     awk '/test/' a.txt

    这里,完整的单词test就属于一种pattern(模式),表示匹配test这个单词,当然,除了用完整的单词作为pattern,我们还可以使用如下正则表达式来作为pattern进行匹配:

普通正则

    
字符

  • 普通字符,包括“test”或者“hello”等

  • 字符类,比如[],所以[abc%8!]表示a或者b或者c或者%或者8或者!中任意一个

  • 范围类,比如-,[a-d]表示一个小写字母a/b/c/d。同理[0-9]表示任意一个数字;[a-z0-9A-Z]表示任意一个字母或数字

  • 反向字符类,用^表示的反向字符类匹配不在字符类中的任意字符。例如正则表达式[^0-9]匹配一个任意非数字字符。

  • 通用类,用.表示除ns之外的任意一个字符

    

    
重复

  • *,表示前面的字符0次或者多次

  • +,表示前面的字符1次或多次

    *注:如果在awk中使用该功能,请参考扩展正则

  • ?,表示前面的字符0次或者1次

    *注:如果在awk中使用该功能,请参考扩展正则

  • {m},表示前面的字符重复m次

    *注:如果在awk中使用该功能,请参考扩展正则,下同

  • {m,},表示前面的字符重复m以及m次以上

  • {,n},表示前面的字符重复m次以内(包括0次)

  • {m,n},表示前面的字符重复m到n次(闭区间,并且n>=m)

    
特殊用法

  • ^,表示行开头,这里需要注意与[]中的^区别,比如^[^abc]表示以非a/b/c字符开头

  • $,表示行结尾

  • |,表示或者

    *注:如果在awk中使用该功能,请参考扩展正则

  • b,表示边界,比如说bthis可以匹配thisabc或者%this或者this,但不能匹配tttthis

    *注:该方法不能在awk中使用

  • ,表示右边界

  • ,用于转义特殊字符

  • (),表示整体(可以改变操作符的优先级)或者将匹配项捕获到12中

    *注:如果在awk中使用该功能,请参考扩展正则。另,awk取出匹配内容不能使用1或者2,而是通过match捕获

  • 1,表示使用(...)所取出来的第一个内容,比如说模式([a-z])1,表示连续的两个相同小写字母

    *注:该方法不能在awk中使用

扩展正则

    
重复

  • {m},表示前面的字符重复m次

  • {m,},表示前面的字符重复m以及m次以上

  • {,n},表示前面的字符重复m次以内(包括0次)

  • {m,n},表示前面的字符重复m到n次(闭区间,并且n>=m)

  • ?,表示前面的字符重复0或者1次,等价于{,1}或者{0,1};同理,*等价于{0,}

  • +,表示前面的字符重复1次或者多次,等价于{1,}

    
特殊用法

  • |,表示或者

  • (),
    表示整体(可以改变操作符的优先级)或者将匹配项捕获到12中

    可以发现,扩展正则是对普通正则的一种简化,为了避免不必要的歧义以及麻烦,建议用户在使用正则时候,直接通过-E参数来在sed/grep中使用扩展正则。

样例

    我们假设有b.txt文件如下:

    [root@n110 ~]# cat b.txt

    xxx1

    xx1

    x1

    x

    x+

    x*

    *.*

    .

    .+

    thisis

    thatis

    thisab

    thatab

    thab

    tatle

    ttle

    hab

    %hab+

    xyz!abc123def

    456xyzabc123def

  • 打印x出现连续3次的行

grep 'x{3}' b.txt #或者 grep -E 'x{3}' b.txt
sed -n '/x{3}/p' b.txt #或者 sed -n -E '/x{3}/p' b.txt
awk '/x{3}/ {print}' b.txt

  • 打印x+英文小写字母+y的行

grep 'x[a-z]y' b.txt
sed -n '/x[a-z]y/p' b.txt
awk '/x[a-z]y' b.txt

  • 打印x+x(出现0次或者多次)+y的行

grep 'xx*y' b.txt
sed -n '/xx*y/p' b.txt
awk '/xx*y/' b.txt

  • 打印x+(x或者y)+x的行

grep 'x[xy]x' b.txt
sed -n '/x[xy]x/p' b.txt
awk '/x[xy]x/' b.txt

  • 打印以xx开头的行

grep '^xx' b.txt
sed -n '/^xx/p' b.txt
awk '/^xx/' b.txt

  • 打印不是以小写字母x或者t开头的行

grep '^[^xt]' b.txt
sed -n '/^[^xt]/p' b.txt
awk '/^[^xt]/' b.txt

  • 打印包括tha或者tat的行

grep 'tha|tat' b.txt  #或者 grep -E 'tha|tat' b.txt
sed -n '/tha|tat/p' b.txt #或者 sed -n -E '/tha|tat/p' b.txt
awk '/tha|tat/' b.txt

  • 打印t+ha或者a+t的行

grep -E 't(ha|a)t' b.txt
sed -n -E '/t(ha|a)t/p' b.txt
awk '/t(ha|a)t/' b.txt

    
*注:此处是()的运用之一,将所括部分当成整体来匹配,当然,括号还可以用以结合1以及2,匹配更多的内容

  • 打印连续两个相同小写字母的行

grep -E '([a-z])1' b.txt
sed -n -E '/([a-z])1/p' b.txt

    
*注:括号部分将一个小写字母选中,后续的1代表该选中内容,如果能够连续匹配,则表示两个相同小写字母

    下面我们来看几个更加复杂的例子。

  • 如果某行匹配连续小写字母+连续数字,那么打印匹配的字母部分

grep -o -E '[a-z]+[0-9]+' b.txt | grep -o -E '[a-z]+' #这里-o参数表示仅输出匹配部分
sed -n -E 's/(.*[^a-z]|^)([a-z]+)[0-9]+.*/2/p' b.txt #这里将整行数据全部替换成取到的2,因为匹配[a-z]+[0-9]+的前一个字符要么是开头,要么是.*加上一个非[a-z]的字符
awk 'match($0,/([a-z]+)[0-9]+/,arr) {print arr[1]}' b.txt #这里使用awk中的match语法,将匹配的内容存放在数组arr中,注意arr[0]代表正则中所能匹配的所有内容,arr[1]代表第一个括号所匹配的内容,arr[2]代表第二个括号匹配内容。

    
*注:如果单行中出现多个[a-z]+[0-9]+,比如说xyz!
abc123def456这种

  • 上述grep命令会将两次
    匹配的[a-z]+[0-9]+中[a-z]+内容打印出来,即abc与def都会打印在不同行。如果需要打印第一个匹配的内容中[a-z]+部分,请使用-m参数控制输出行

  • 上述sed命令会打印第二次匹配的[a-z]+[0-9]+中[a-z]+内容,也就是字符串def(.*的贪婪匹配)

  • 上述awk命令仍然只会打印第一次匹配到的内容

  • 打印is是右边界的行

grep 'isb' b.txt 或者 grep 'is>' b.txt
sed -n 's/isb/p' b.txt 或者 sed -n 's/is>/p' b.txt
awk '/is>' b.txt

  • 打印仅匹配单词hab的行(注:连续字母thab不能被打印)

grep 'bhabb' b.txt
grep '' b.txt
sed -n '/bhabb/p' b.txt
awk '//' b.txt
grep -w 'hab' b.txt # 直接使用-w参数(word)

  • 打印x+a或者b出现0次或多次+y

grep -E 'x[ab]{0,}y' b.txt
grep 'x[ab]*y' b.txt
sed -n '/x[ab]*y/p' b.txt
sed -n -E  '/x[ab]{0,}y/p' b.txt

  • 将第一次出现的连续字母+连续数字的位置对调,并打印匹配行中对调后内容

sed -n -E 's/([a-z]+)([0-9]+)/21/p' b.txt
awk 'match($0,/([a-z]+)([0-9]+)/,arr) {print arr[2] arr[1]}' b.txt

    以上内容基本涵盖了常用的正则匹配,但在实际使用当中,还是需要将正则内容组合起来,实现更复杂内容的匹配与选取。我们再看下面几个例子:

    [root@n110 ~]# cat d.txt

    this is cat!

    this is THE cat!

    this is THE THE cat!

    THE THE THE cat!

    this is THE THE THE cat!

    this is THE THAT THE cat!

    this is THE THE THE THE cat!

    this is THE THE THE THE THEcat!

    this isTHE THE THE THE THEcat!

    this is A A A cat!

    123

    thisis123

  • 去掉d.txt中重复的独立单词THE

    第一反应通常是将连续的THE替换成第一个,我们尝试命令如下:

sed -E 's/(THE )+/1/g' d.txt

    上述红框内容出现了异常,按理说原有该行内容为“
this isTHE THE THE THE THEcat!”,去除完独立THE之后,应该剩余“
this isTHE THE THEcat!”,出现红框中原因是isTHE也被当成一个独立的THE,所以为了避免该错误,我们改进命令如下:

sed -E 's/b(THE )+/1/g' d.txt

    此时显示正确!

    另外,该命令还可以使用如下命令代替:

sed -E 's/b(THE )1+/1/g' d.txt #先出现一个独立的THE,再用1代表该内容,使用+表示该内容出现1次或者多次,最后替换成1,也就是不管几次匹配,最后都替换成一次匹配

  • 去掉d.txt中所有重复的独立单词(依据d.txt内容来看,不仅需要去除连续单词THE,还要去除连续单词A)

    第一个想到的方法,就是将连续的单词替换成1:

sed -E 's/

相关文章

服务器端口转发,带你了解服务器端口转发
服务器开放端口,服务器开放端口的步骤
产品推荐:7月受欢迎AI容器镜像来了,有Qwen系列大模型镜像
如何使用 WinGet 下载 Microsoft Store 应用
百度搜索:蓝易云 – 熟悉ubuntu apt-get命令详解
百度搜索:蓝易云 – 域名解析成功但ping不通解决方案

发布评论