1 正则表达式介绍
用一个命令查找一个模式得到一类字符串时,这个模式应该包含以下信息:
- 字符类:它们在模式中表示一个字符,取值范围是一类字符中的任意一个。
- 数量限定符:如IP地址的每一部分可以有1 –3个数字。
- 各种字符类以及普通字符之间的位置关系:如IP地址分四部分,用普通字符’.’隔开,每部分都可以用字符类和数量限定符描述。
规定一些特殊语法表示字符类、数量限定符和位置关系,然后用这些特殊语法和普通字符一起表示一个模式,这就是正则表达式(Regular Expression)。
2 grep正则表达式的Extended规范
各种工具和编程语言所使用的正则表达式规范的语法并不相同,表达能力也各不相同,有的正则表达式规范引入很多扩展,能表达更复杂的模式,但各种正则表达式规范的基本概念都是相通的。grep所使用的正则表达式,它大致上符合POSIX正则表达式规范,此笔记学习、练习grep使用的正则表达式。
(1) 字符类
字符 |
含义 |
举例 |
. |
匹配任意一个字符 |
abc.可以匹配abcd、abc9等 |
[] |
匹配括号中的任意一个字符 |
[abc]d可以匹配ad、bd或cd |
- |
在[]括号内表示字符范围 |
[0-9a-fA-F]可以匹配一位十六进制数字 |
^ |
位于[]括号内的开头,匹配除括号中的字符之外的任意一个字符 |
[^xy]匹配除xy之外的任一字符,因此[^xy]1可以匹配 a1、b1但不匹配x1、y1 |
[[:xxx:]] |
grep工具预定义的一些命名字符类 |
[[:alpha:]]匹配一个字母, [[:digit:]]匹配一个数字 |
(2) 数量限定符
字符 |
含义 |
举例 |
? |
紧跟在它前面的单元应匹配零次或一次 |
[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}匹配IP地址 |
(3) 位置限定符
字符 |
含义 |
举例 |
^ |
匹配行首的位置 |
^Content匹配位于一行开头的Content |
$ |
匹配行末的位置 |
;$匹配位于一行结尾的;号,^$匹配空行 |
\< |
匹配单词开头的位置 |
\<th匹配... this,但不匹配ethernet、tenth |
\> |
匹配单词结尾的位置 |
p\>匹配leap ...,但不匹配parent、sleepy |
\b |
匹配单词开头或结尾的位置 |
\bat\b匹配... at ...,但不匹配cat、atexit、batch |
\B |
匹配非单词开头和结尾的位置 |
\Bat\B匹配battery,但不匹配... attend、hat... |
(4) 其它特殊字符
字符 |
含义 |
举例 |
\ |
转义字符,普通字符转义为特殊字符,特殊字符转义为普通字符 |
普通字符<写成\<表示单词开头的位置,特殊字符.写成\.以及\写成\\就当作普通字符来匹配 |
() |
将正则表达式的一部分括起来组成一个单元,可以对整个单元使用数量限定符 |
([0-9]{1,3}\.){3}[0-9]{1,3}匹配IP地址 |
| |
连接两个子表达式,表示或的关系 |
n(o|either)匹配no或neither |
Basic规范也有这些语法,只是字符?+{}|()应解释为普通字符,要表示上述特殊含义则需要加\转义。如果用grep而不是egrep,并且不加-E参数,则应该遵照Basic规范来写正则表达式。
3 sed与正则表达式
(1) sed简介
sed意为流编辑器(Stream Editor),在Shell脚本和Makefile中作为过滤器使用非常普遍,也就是把前一个程序的输出引入sed的输入,经过一系列编辑命令转换为另一种格式输出。
sed命令不会修改原文件,只是经过sed命令后输出需要部分。
(2) sed涉及的正则表达式及编辑命令
sed处理的文件既可以由标准输入重定向得到,也可以当命令行参数传入,命令行参数可以一次传入多个文件,sed会依次处理。sed的编辑命令可以直接当命令行参数传入,也可以写成一个脚本文件然后用-f参数指定,编辑命令的格式为:
/pattern/action |
其中pattern是正则表达式,action是编辑命令。sed一行一行读出待处理文件,如果某一行与pattern匹配,则执行相应的action,如果一条命令没有pattern而只有action,这个action将作用于待处理文件的每一行。
sed常用编辑操作:
/pattern/p |
打印匹配pattern的行 |
/pattern/d |
删除匹配pattern的行 |
/pattern/s/pattern1/pattern2/ |
查找符合pattern的行,将该行第一个匹配pattern1的字符串替换为pattern2 |
/pattern/s/pattern1/pattern2/g |
查找符合pattern的行,将该行所有匹配 pattern1的字符串替换为pattern2 |
(3) sed命令行格式
根据(2)中对sed处理的文件和编辑命令的描述可得sed命令行的基本格式为:
sed option 'script' file1 file2 ... sed option –f scriptfile file1 file2 … |
- option表示sed的可选参数,如要想只输出处理结果则option为-n。
- ‘script’表示脚本命令[ (2)中提到的编辑命令 ]。
- filen表示要用sed处理的文件。
- -f属option范畴,它允许将脚本命令编写成脚本文件再传给sed。
4 awk
(1) awk介绍
sed以行为单位处理文件,awk比sed强的地方在于不仅能以行为单位还能以列为单位处理文件。awk缺省的行分隔符是换行,缺省的列分隔符是连续的空格和Tab,但是行分隔符和列分隔符都可以自定义,比如/etc/passwd文件的每一行有若干个字段,字段之间以:分隔,就可以重新定义awk的列分隔符为:并以列为单位处理这个文件。
(2) awk命令行格式
awk基本用法和sed类似,awk命令行的基本形式为:
awk option 'script' file1 file2 .. awk option -f scriptfile file1 file2 ... |
和sed一样,awk处理的文件既可以由标准输入重定向得到,也可以当命令行参数传入,编辑命令可以直接当命令行参数传入,也可以用-f参数指定一个脚本文件,编辑命令的格式为:
/pattern/{actions} condition{actions} |
pattern是正则表达式,actions是一系列操作(编辑命令),awk程序一行一行读出待处理文件,如果某一行与pattern匹配,或者满足condition条件,则执行相应的actions,如果一条awk命令只有actions部分,则actions作用于待处理文件的每一行。
awk命令的condition部分还可以是两个特殊的condition-BEGIN和END,对于每个待处理文件,BEGIN后面的actions在处理整个文件之前执行一次,END后面的actions在整个文件处理完之后执行一次。
(3) awk内建变量
awk命令可以像C语言一样使用变量(但不需要定义变量)。就像Shell的环境变量一样,有些awk变量是预定义的有特殊含义的:
FILENAME |
|
NR |
当前行的行号,该变量是只读的,R代表record |
NF |
当前行所拥有的列数,该变量是只读的,F代表field |
OFS |
输出格式的列分隔符,缺省是空格 |
FS |
输入文件的列分融符,缺省是连续的空格和Tab |
ORS |
输出格式的行分隔符,缺省是换行符 |
RS |
输入文件的行分隔符,缺省是换行符 |
awk还可以像C语言一样使用if/else、while、for控制结构。以前未“系统”掌握awk命令格式时做的笔记(例子):shell awk读取文件中的指定行的指定字段。
5 在C语言中使用的正则表达式
POSIX规定了正则表达式的C语言库函数:regex(3)。man regex。
用正则表达式匹配字符串是否为IP地址的C代码:
/*Filename: regex_in_c.c *Brife: use c function to use regex *Author: One fish *Date: 2014.8.12 Tuesday */ #include <sys/types.h> #include <regex.h> #include <stdio.h> #include <stdlib.h> int main(void) { regex_t reg; char regex[] = "[0-9]{1,3}\\.[0-9]{1,3}"; char str[] = "172.168.13.1"; size_t nmatch = 1; regmatch_t pmatch[1]; char errbuf[100]; int i,re; //Compile regex to reg re = regcomp(®,regex,REG_EXTENDED | REG_NEWLINE); if ( 0 != re ) { regerror(re,®,errbuf,100); printf("%s\n",errbuf); exit(1); } //Use reg which compiled from pattern to match substring in whole string str re = regexec(®,str,nmatch,pmatch,0); if (REG_NOMATCH == re) { printf("No match\n"); exit(1); } else if (0 == re) { printf("matched:\n"); for (i = pmatch[0].rm_so; i < pmatch[0].rm_eo; ++i) { putchar(str[i]); } printf("\n"); printf("%d,%d\n",pmatch[0].rm_so,pmatch[0].rm_eo); } //Free ® used in recomp regfree(®); return 0; }
编译并执行程序:
gcc -o regex_in_c regex_in_c.c ./regex_in_c matched: 172.168.13.1 0,12 |
当在regcomp函数中的cflags参数中指定REG_NOSUB时nmatch和pmatch参数将会被忽略,不能再将它们的值输出来。
[2014.8.11-21.02 ---- 2014.8.12-16.29]
LCNote Over.