正则表达式

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:

  1. [root@VM_0_5_centos test]# vi testgrep.file
  2. [root@VM_0_5_centos test]# cat testgrep.file
  3. 192.168.13.108
  4. 123.5254.045.678
  5. abcde52s
  6. 198.23.233.342
  7. 1233.232.232.4

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

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

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

  1. grep的正则表达式有BasicExtended两种规范:
  2. 1grep正则表达式的Extended规范在基本语法中介绍;
  3. 2Basic规范也有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:]]匹配一个数字

数量限定符:

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

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

  1. [root@VM_0_5_centos test]# vi testfile.txt
  2. [root@VM_0_5_centos test]# cat testfile.txt
  3. acaabc
  4. caad
  5. efg
  6. sdcasd
  7. sda

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

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

位置限定符:

字符 含义 举例
^
  1. 匹配行首的位置
  1. ^Content匹配位于一行开头的Content
$
  1. 匹配行末的位置
  1. ;$匹配位于一行结尾的;号,^$匹配空行
\<
  1. 匹配单词开头的位置
  1. \<th匹配... this,但不匹配ethernettenth
\>
  1. 匹配单词结尾的位置
  1. p\>匹配leap ...,但不匹配parentsleepy
\b
  1. 匹配单词开头或结尾的位置
  1. \bat\b匹配... at ...,但不匹配catatexitbatch
\B
  1. 匹配非单词开头和结尾的位置
  1. \Bat\B匹配battery,但不匹配... attendhat ...

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

  1. 192.168.13.108
  2. 198.23.233.342
  3. 1233.232.232.4

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

其它特殊字符:

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

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

3、grep

1.作用

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

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

2.格式

  1. grep [options]

3.主要参数

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

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

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

4.grep命令使用简单实例

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

5.grep命令使用复杂实例

明确要求搜索子目录:

  1. grep -r

或忽略子目录:

  1. grep -d skip

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

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

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

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

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

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

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

  1. \< \> 分别标注单词的开始与结尾
  2. 例如:
  3. grep man * 会匹配 Batman’、’manic’、’man’等;
  4. grep \<man * 会匹配’manic’和’man’,但不是’Batman’;
  5. grep \<man\> 只匹配’man’,而不是’Batman’或’manic’等其他的字符串。
  6. ‘^’ 指匹配的字符串在行首;
  7. $ 指匹配的字符串在行尾;
  8. 例如:
  9. [root@VM_0_5_centos test]# cat testfile.txt
  10. acaabc
  11. caad
  12. efg
  13. sdcasd
  14. sda
  15. [root@VM_0_5_centos test]# grep '^a' testfile.txt
  16. acaabc
  17. [root@VM_0_5_centos test]# grep -E '^a' testfile.txt
  18. acaabc
  19. [root@VM_0_5_centos test]# grep -E 'a$' testfile.txt
  20. sda

shell编程基础(五): 正则表达式及其使用的更多相关文章

  1. shell编程基础(转载)

    Shell编程基础 原作者 Leal:请参阅页面底部的编者列表. 授权许可: 创作共享署名协议 GNU 自由文档许可证 注意:本文仍然在持续的修订之中,且错漏之处可能较多.如果能够阅读英语的话,可以考 ...

  2. 7-1 shell编程基础之二

    shell编程基础之二 算数运算 bash中的算术运算:help let +, -, *, /, %取模(取余), **(乘方),乘法符号有些场景中需要转义 实现算术运算: (1) let var=算 ...

  3. 【转】Shell编程基础篇-下

    [转]Shell编程基础篇-下 1.1 条件表达式 1.1.1 文件判断 常用文件测试操作符 常用文件测试操作符 说明 -d文件,d的全拼为directory 文件存在且为目录则为真,即测试表达式成立 ...

  4. 【转】Shell编程基础篇-上

    [转]Shell编程基础篇-上 1.1 前言 1.1.1 为什么学Shell Shell脚本语言是实现Linux/UNIX系统管理及自动化运维所必备的重要工具, Linux/UNIX系统的底层及基础应 ...

  5. 【Shell 编程基础第二部分】Shell里的流程控制、Shell里的函数及脚本调试方法!

    http://blog.csdn.net/xiaominghimi/article/details/7603003 本站文章均为李华明Himi原创,转载务必在明显处注明:转载自[黑米GameDev街区 ...

  6. Linux学习之二十一-shell编程基础

    Shell编程基础 Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁.Shell 既是一种命令语言,又是一种程序设计语言.Shell 是指一种应用程序,这个应用程序提供了一个 ...

  7. 6-2 shell编程基础

    shell编程基础 编程基础 Linus:Talk is cheap, show me the code 程序和编程风格 程序: 程序:算法+数据结构 数据:是程序的核心 算法:处理数据的方式 数据结 ...

  8. Linux(五)shell编程基础

    一.Linux shell简介 1.shell概述 Shell 是用户与内核进行交互操作的一种接口,目前最流行的 Shell 称为 bash Shell          Shell 是一门编程语言& ...

  9. 基于Linux系统的Shell编程-基础篇

    1. Shell基础介绍 1.1 Shell编程的意义 为什么使用shell编程 节约时间 1.2 显示脚本执行过程 前面有+表示执行过的命令的 前面没有东西,表示输出到屏幕上的内容. [root@C ...

随机推荐

  1. orm介绍

    昨日回顾: 1 今日内容: 1 orm介绍 1 tools--->Run manage.py Task python3 manage.py makemigrations 只需要敲命令:makem ...

  2. Oracle EBS数据定义移植工具:Xdf(XML Object Description File)

    转载自:http://www.orapub.cn/posts/3296.html Oracle EBS二次开发中,往往会创建很多数据库对象,如表.同义词.视图等,这些数据库对象是二次开发配置管理内容很 ...

  3. MySQL--REPLACE INTO与自增

    ##=====================================================================##测试环境:MySQL版本:MySQL 5.7.19复制 ...

  4. 包建强的培训课程(6):Android App瘦身优化

    v\:* {behavior:url(#default#VML);} o\:* {behavior:url(#default#VML);} w\:* {behavior:url(#default#VM ...

  5. 【高速接口-RapidIO】2、RapidIO串行物理层的包与控制符号

    一.RapidIO串行物理层背景介绍 上篇博文提到RapidIO的物理层支持串行物理层与并行物理层两种,由于Xilinx 部分FPGA内部已经集成了串行高速收发器,所以用FPGA实现RapidIO大多 ...

  6. Open-Source Cybersecurity Infrastructure

    https://www.linkedin.com/pulse/open-source-cybersecurity-infrastructure-adrian/ The increased maturi ...

  7. python爬虫学习之正则表达式的基本使用

    一.正则表达式 1. 正则表达式是字符串处理的有力工具和技术. 2. 正则表达式使用某种预定义的模式去匹配一类具有共同特征的字符串,主要用于处理字符串,可以快速.准确地完成复杂的查找.替换等处理要求, ...

  8. Android JNI 学习(一):JNI 简介

    JNI 即 Java Native Interface 是 native 编程接口,它允许在Java虚拟机(VM)内运行Java代码与其他编程语言(主要是C和C++)编写的应用程序和库进行交互操作. ...

  9. Android Studio 调试各种国产手机经验总结

    为何加上“国产”二字呢,因为目前测试时就国产手机存在的安装问题多,而且都很奇葩,不得不说对于开发者时很不友好的. 下面就是个人总结的针对不同的机型调试时出现的问题做的总结: 1.VIVO 手机 解决方 ...

  10. 生成uuid唯一标识符

    generate_uuid: function(){ var d = new Date().getTime(); if(window.performance && typeof win ...