• shell编程基础(五): 正则表达式及其使用


    正则表达式

    1、前情提要

      以前我们用grep在一个文件中找出包含某些字符串的行,比如在头文件中找出一个宏定义。其实grep还可以找出符合某个模式(Pattern)的一类字符串。例如找出所有符合xxxxx@xxxx.xxx模式的字符串(也就是email地址),要求x字符可以是字母、数字、下划线、小数点或减号,email地址的每一部分可以有一个或多个x字符,例如abc.d@ef.com、1_2@987-6.54,当然符合这个模式的不全是合法的email地址,但至少可以做一次初步筛选,筛掉a.b、c@d等肯定不是email地址的字符串。再比如,找出所有符合yyy.yyy.yyy.yyy模式的字符串(也就是IP地址),要求y是0-9的数字,IP地址的每一部分可以有1-3个y字符。

    如果要用grep查找一个模式,如何表示这个模式,这一类字符串,而不是一个特定的字符串呢?从这两个简单的例子可以看出,要表示一个模式至少应该包含以下信息:

      字符类(Character Class):如上例的x和y,它们在模式中表示一个字符,但是取值范围是一类字符中的任意一个。

      数量限定符(Quantifier): 邮件地址的每一部分可以有一个或多个x字符,IP地址的每一部分可以有1-3个y字符

    各种字符类以及普通字符之间的位置关系:例如邮件地址分三部分,用普通字符@和.隔开,IP地址分四部分,用.隔开,每一部分都可以用字符类和数量限定符描述。

    为了表示位置关系,还有位置限定符(Anchor)的概念,将在下面介绍。

    规定一些特殊语法表示字符类、数量限定符和位置关系,然后用这些特殊语法和普通字符一起表示一个模式,这就是正则表达式(Regular Expression)。

    例如email地址的正则表达式可以写成[a-zA-Z0-9_.-]+@[a-zA-Z0-9_.-]+.[a-zA-Z0-9_.-]+,IP地址的正则表达式可以写成[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}。

    等下介绍正则表达式的语法,我们先看看正则表达式在grep中怎么用。例如有这样一个文本文件testgrep:

    [root@VM_0_5_centos test]# vi testgrep.file 
    [root@VM_0_5_centos test]# cat testgrep.file 
    192.168.13.108
    123.5254.045.678
    abcde52s
    198.23.233.342
    1233.232.232.4

    查找其中包含IP地址的行:

    注:1、egrep相当于grep -E,表示采用Extended正则表达式语法。另外还有fgrep命令,相当于grep -F,表示只搜索固定字符串而不搜索正则表达式模式,不会按正则表达式的语法解释后面的参数。

    2、正则表达式参数用单引号括起来了,因为正则表达式中用到的很多特殊字符在Shell中也有特殊含义(例如),只有用单引号括起来才能保证这些字符原封不动地传给grep命令,而不会被Shell解释掉。

    grep的正则表达式有Basic和Extended两种规范:
    1、grep正则表达式的Extended规范在基本语法中介绍;
    2、Basic规范也有Extended规范的这些语法,只是字符?+{}|()应解释为普通字符,要表示上述特殊含义则需要加转义。如果用grep而不是egrep,并且不加-E参数,则应该遵照Basic规范来写正则表达式。

    问:192.168.13.108符合上述模式,由三个.隔开的四段组成,每段都是1到3个数字,所以这一行被找出来了,可为什么1233.232.232.4也被找出来了呢?

    答:因为grep找的是包含某一模式的行,这一行包含一个符合模式的字符串233.232.232.4。相反,123.5254.045.678这一行不包含符合模式的字符串,所以不会被找出来。

      grep是一种查找过滤工具,正则表达式在grep中用来查找符合模式的字符串。其实正则表达式还有一个重要的应用是验证用户输入是否合法,例如用户通过网页表单提交自己的email地址,就需要用程序验证一下是不是合法的email地址,这个工作可以在网页的Javascript中做,也可以在网站后台的程序中做,例如PHP、Perl、Python、Ruby、Java或C,所有这些语言都支持正则表达式,可以说,目前不支持正则表达式的编程语言实在很少见。除了编程语言之外,很多UNIX命令和工具也都支持正则表达式,例如grep、vi、sed、awk、emacs等等。“正则表达式”就像“变量”一样,它是一个广泛的概念,而不是某一种工具或编程语言的特性。

    2、基本语法

      我们知道C的变量和Shell脚本变量的定义和使用方法很不相同,表达能力也不相同,C的变量有各种类型,而Shell脚本变量都是字符串。同样道理,各种工具和编程语言所使用的正则表达式规范的语法并不相同,表达能力也各不相同,有的正则表达式规范引入很多扩展,能表达更复杂的模式,但各种正则表达式规范的基本概念都是相通的。本节介绍egrep(1)所使用的正则表达式,它大致上符合POSIX正则表达式规范,详见regex(7)(看这个man page对你的英文绝对是很好的锻炼)。希望读者仿照上一节的例子,一边学习语法,一边用egrep命令做实验。

    字符类:

    字符 含义 举例
    . 匹配任意一个字符  abc.可以匹配abcd、abc9等
    [] 匹配括号中的任意一个字符 [abc]d可以匹配ad、bd或cd
    - 在[]括号内表示字符范围  [0-9a-fA-F]可以匹配一位十六进制数字
    ^ 位于[]括号内的开头,匹配除括号中的字符之外的任意一个字符  [^xy]匹配除xy之外的任一字符,因此[^xy]1可以匹配a1、b1但不匹配x1、y1
    [[:xxx:]] grep工具预定义的一些命名字符类  [[:alpha:]]匹配一个字母,[[:digit:]]匹配一个数字

    数量限定符:

    字符 含义 举例
    ?
    紧跟在它前面的单元应匹配零次或一次
    [0-9]?.[0-9]匹配0.0、2.3、.5等,由于.在正则表达式中是一个特殊字符,所以需要用转义一下,取字面值
    +
    紧跟在它前面的单元应匹配一次或多次   
    [a-zA-Z0-9_.-]+@[a-zA-Z0-9_.-]+.[a-zA-Z0-9_.-]+匹配email地址
    *
    紧跟在它前面的单元应匹配零次或多次
    [0-9][0-9]*匹配至少一位数字,等价于[0-9]+,[a-zA-Z_]+[a-zA-Z_0-9]*匹配C语言的标识符
    {N}
    紧跟在它前面的单元应精确匹配N次  
    [1-9][0-9]{2}匹配从100到999的整数
    {n,}
    紧跟在它前面的单元应匹配至少N次   
    [1-9][0-9]{2,}匹配三位以上(含三位)的整数
    {,M}
    紧跟在它前面的单元应匹配最多M次 
    [0-9]{,1}相当于[0-9]?
    {N,M}
    紧跟在它前面的单元应匹配至少N次,最多M次  
    [0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}匹配IP地址

    再次注意grep找的是包含某一模式的行,而不是完全匹配某一模式的行。再举个例子,如果文本文件的内容是:

    [root@VM_0_5_centos test]# vi testfile.txt
    [root@VM_0_5_centos test]# cat testfile.txt 
    acaabc
    caad
    efg
    sdcasd
    sda

    查找a*这个模式的结果是三行都被找出来了:

    注:a匹配0个或多个a,而第三行包含0个a,所以也包含了这一模式。单独用a这样的正则表达式做查找没什么意义,一般是把a*作为正则表达式的一部分来用。

    位置限定符:

    字符 含义 举例
    ^
    匹配行首的位置
    ^Content匹配位于一行开头的Content
    $
    匹配行末的位置       
     ;$匹配位于一行结尾的;号,^$匹配空行
    <
    匹配单词开头的位置  
    <th匹配... this,但不匹配ethernet、tenth
    >
    匹配单词结尾的位置    
    p>匹配leap ...,但不匹配parent、sleepy
    
    匹配单词开头或结尾的位置     
    at匹配... at ...,但不匹配cat、atexit、batch
    B
    匹配非单词开头和结尾的位置  
     BatB匹配battery,但不匹配... attend、hat ...

    位置限定符可以帮助grep更准确地查找,例如上一节我们用[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}查找IP地址,找到这两行:

    192.168.13.108
    198.23.233.342
    1233.232.232.4

    如果用^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$查找,就可以把1233.232.232.4这一行过滤掉了。

    其它特殊字符:

    字符 含义 举例
    转义字符,普通字符转义为特殊字符,特殊字符转义为普通字符 
    普通字符<写成<表示单词开头的位置,特殊字符.写成.以及写成\就当作普通字符来匹配
    ()
     将正则表达式的一部分括起来组成一个单元,可以对整个单元使用数量限定符
    ([0-9]{1,3}.){3}[0-9]{1,3}匹配IP地址
    |
     连接两个子表达式,表示或的关系
    n(o|either)匹配no或neither

    以上介绍的是grep正则表达式的Extended规范,Basic规范也有这些语法,只是字符?+{}|()应解释为普通字符,要表示上述特殊含义则需要加转义。如果用grep而不是egrep,并且不加-E参数,则应该遵照Basic规范来写正则表达式。

    3、grep

    1.作用

    Linux系统中grep命令是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹 配的行打印出来。grep全称是Global Regular Expression Print,表示全局正则表达式版本,它的使用权限是所有用户。

    rep家族包括grep、egrep和fgrep:
    egrep和fgrep的命令只跟grep有很小不同。
    egrep是grep的扩展,支持更多的re元字符;
    fgrep就是fixed grep或fast grep,它们把所有的字母都看作单词,也就是说,正则表达式中的元字符表示回其自身的字面意义,不再特殊。
    linux使用GNU版本的grep。它功能更强,可以通过-G、-E、-F命令行选项来使用egrep和fgrep的功能。

    2.格式

    grep [options]

    3.主要参数

    grep --help
    
    [options]主要参数:
    -c:只输出匹配行的计数。
    -i:不区分大小写。
    -h:查询多文件时不显示文件名。
    -l:查询多文件时只输出包含匹配字符的文件名。
    -n:显示匹配行及行号。
    -s:不显示不存在或无匹配文本的错误信息。
    -v:显示不包含匹配文本的所有行。
    --color=auto :可以将找到的关键词部分加上颜色的显示。

    pattern正则表达式主要参数:(pattern部分最好用双引号)

          忽略正则表达式中特殊字符的原有含义。
    ^      匹配正则表达式的开始行。
    $      匹配正则表达式的结束行。
    <     从匹配正则表达 式的行开始。
    >     到匹配正则表达式的行结束。
    [ ]     单个字符,如[A]即A符合要求 。
    [ - ]   范围,如[A-Z],即A、B、C一直到Z都符合要求 。
    .      所有的单个字符。
    *      有字符,长度可以为0。

    4.grep命令使用简单实例

    显示所有以d开头的文件中包含 test的行;
    [root@VM_0_5_centos test]# grep “test” d*
    
    显示在aa,bb,cc文件中匹配test的行(./*表示当前目录下的所有文件);
    [root@VM_0_5_centos test]# grep ‘test’ aa bb cc
    [root@VM_0_5_centos test]# egrep "sda" ./*
    ./test:sda
    ./testfile.txt:sda
    ./testsd:sdadf
    [root@VM_0_5_centos test]# egrep "sda" ./* -n
    ./test:2:sda
    ./testfile.txt:5:sda
    ./testsd:2:sdadf
    [root@VM_0_5_centos test]# egrep "sda" ./* -c
    ./test:1
    ./testfile.txt:1
    ./testgrep.file:0
    ./testsd:1
    ./tfun.sh:0
    
    显示所有包含每个字符串至少有5个连续小写字符的字符串的行;
    [root@VM_0_5_centos test]# grep “[a-z]{5}” test
    [root@VM_0_5_centos test]# egrep “[a-z]{5}” test
    
    如果asd被匹配,则s就被存储到内存中,并标记为1,然后搜索任意个字符(.*),这些字符后面紧跟着另外一个s(1),找到就显示该行。如果用egrep或grep -E,就不用””号进行转义,直接写成’a(s)d.*1′就可以了;
    [root@VM_0_5_centos test]# grep “a(s)d.*1” test

    5.grep命令使用复杂实例

    明确要求搜索子目录:

    grep -r

    或忽略子目录:

    grep -d skip

    如果有很多输出时,您可以通过管道将其转到’less’上阅读:

    IP地址的正则表达式可以写成[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}。
    grep -E '^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$' testgrep.file | less

    这样,您就可以更方便地阅读。

    注意:您必需提供一个文件过滤方式(搜索全部文件的话用 *)。如果您忘了,’grep’会一直等着,直到该程序被中断。如果您遇到了这样的情况,按 ,然后再试。

    下面还有一些有意思的命令行参数:

    grep -i pattern files  //不区分大小写地搜索。默认情况区分大小写,
    grep -l pattern files  //只列出匹配的文件名,
    grep -L pattern files  //列出不匹配的文件名,
    grep -w pattern files  //只匹配整个单词,而不是字符串的一部分(如匹配’magic’,而不是’magical’),
    grep -C number pattern files//匹配的上下文分别显示[number]行,
    grep pattern1 | pattern2 files //显示匹配 pattern1 或 pattern2 的行,
    例如:
    grep "abc|xyz" testfile    表示过滤包含abc或xyz的行
    grep pattern1 files | grep pattern2 //显示既匹配 pattern1 又匹配 pattern2 的行。
    grep -n pattern files  //即可显示行号信息
    grep -c pattern files  //即可查找总行数

    这里还有些用于搜索的特殊符号:

    < 和 >     分别标注单词的开始与结尾
    例如:
    grep man *     会匹配 ‘Batman’、’manic’、’man’等;
    grep ‘<man’ *     会匹配’manic’和’man’,但不是’Batman’;
    grep ‘<man>’     只匹配’man’,而不是’Batman’或’manic’等其他的字符串。
    ‘^’    指匹配的字符串在行首;
    ‘$’    指匹配的字符串在行尾;
    例如:
    [root@VM_0_5_centos test]# cat testfile.txt 
    acaabc
    caad
    efg
    sdcasd
    sda
    [root@VM_0_5_centos test]# grep '^a' testfile.txt 
    acaabc
    [root@VM_0_5_centos test]# grep -E '^a' testfile.txt 
    acaabc
    [root@VM_0_5_centos test]# grep -E 'a$' testfile.txt 
    sda
  • 相关阅读:
    洛谷-P1496 火烧赤壁
    洛谷-P5638 【CSGRound2】光骓者的荣耀
    Java多线程实现
    linux安装mysql 8的完整过程
    tomcat安装
    C#之Null
    JToken
    初识算法之线性表---双向列表
    初识算法之线性表---链表定义/结构(2)
    初识算法之线性表---链表定义/结构(1)
  • 原文地址:https://www.cnblogs.com/mmzs/p/9317987.html
Copyright © 2020-2023  润新知