grep号称文本处理三剑客之一,虽然说是三剑客中功能最少也是最简单的,但仍然不能小觑。
grep的全称为:Global search REgular expression and Print out the line,即:全面查找正则表达式并将匹配的行显示出来。
那么这里面出现了一个新的概念:正则表达式。那么什么是正则表达式呢?
正则表达式,又称正规表示法、常规表示法英语:(Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正则表达式使用单个字符串来描述、匹配一系列符合某个句法规则。在很多文本编辑器里,正则表达式通常被用来检索、替换那些符合某个模式的文本。
正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个"规则字符串",这个"规则字符串"用来表达对字符串的一种过滤逻辑。
给定一个正则表达式和另一个字符串,我们可以达到如下的目的:
1.给定的字符串是否符合正则表达式的过滤逻辑(称作“匹配”);
2.可以通过正则表达式,从字符串中获取我们想要的特定部分。
正则表达式的特点是:
1.灵活性、逻辑性和功能性都非常的强;
2.可以迅速地用极简单的方式达到字符串的复杂控制。
3.对于刚接触的人来说,比较晦涩难懂。
由于正则表达式主要应用对象是文本,因此它在各种文本编辑器场合都有应用。许多程序设计语言都支持利用正则表达式进行字符串操作。例如,在Perl中就内建了一个功能强大的正则表达式引擎——PCRE;此外,还有java语言自带的。
主流的正则引擎又为3类:一、DFA,二、传统型NFA,三、POSIX NFA。
DFA 引擎在线性时状态下执行,因为它们不要求回溯(并因此它们永远不测试相同的字符两次)。DFA引擎还可以确保匹配最长的可能的字符串。但是,因为DFA引擎只包含有限的状态,所以它不能匹配具有反向引用的模式;并且因为它不构造显示扩展,所以它不可以捕获子表达式。
传统的NFA引擎运行所谓的“贪婪的”匹配回溯算法,以指定顺序测试正则表达式的所有可能的扩展并接受第一个匹配项。因为传统的 NFA构造正则表达式的特定扩展以获得成功的匹配,所以它可以捕获子表达式匹配和匹配的反向引用。但是,因为传统的NFA回溯,所以它可以访问完全相同的状态多次(如果通过不同的路径到达该状态)。因此,在最坏情况下,它的执行速度可能非常慢。因为传统的NFA接受它找到的第一个匹配,所以它还可能会导致其他(可能更长)匹配未被发现。
POSIX NFA引擎与传统的NFA引擎类似,不同的一点在于:在它们可以确保已找到了可能的最长的匹配之前,它们将继续回溯。因此,POSIX NFA引擎的速度慢于传统的NFA引擎;并且在使用POSIX NFA时,您恐怕不会愿意在更改回溯搜索的顺序的情况下来支持较短的匹配搜索,而非较长的匹配搜索。
使用DFA引擎的程序主要有:awk, egrep, flex, lex, MySQL, Procmail等;
使用传统型NFA引擎的程序主要有:GNU Emacs, Java, ergp, less, more, .NET语言, PCRE library, Perl, PHP, Python, Ruby, sed, vi, vim;
使用POSIX NFA引擎的程序主要有:mawk, Mortice Kern Systems’ utilities, GNU Emacs(使用时可以明确指定);
也有使用DFA/NFA混合的引擎:GNU awk, GNU grep/egrep, Tcl。
有了上述的一点知识,接下来继续跟大家一起来讨论grep就简单些了。为了能够满足不同用户的不同需求,grep家族中已经有了三个成员,即:grep、egrep、fgrep。其中grep是最基本的一个功能实现,如果想要更加简单的使用扩展的正则表达式,使用egrep就可以了;如果想要快速实现查找而不需要使用正则表达式的匹配的话,就要选择fgrep了。
不管使用哪个命令,我们的目的都是唯一的:正确的从指定文件中查找得到我们感兴趣的那些行。
下面介绍以下grep命令的使用方法:
grep:外部命令
功能:从源数据中显示出被模式匹配到的行。而且,grep默认工作在贪婪模式下,也就是说,每次grep进行模式匹配都是按照尽可能多的去完成匹配的标准进行的。
格式:
grep [OPTIONS] PATTERN [FILE...]
grep [OPTIONS] [-e PATTERN | -f FILE] [FILE...]
常用选项:
-A NUM, --after-context=NUM:被模式匹配到的行以及其后#行,如果后面没有,则不显示;不能和-o选项一同使用
-B NUM, --before-context=NUM: 被模式匹配到的行以及其前#行,如果前面没有,则不显示;不能和-o选项一同使用
-C NUM, -NUM, --context=NUM:被模式匹配到的行以及其前后各#行,如果前面或后面没有,则不显示;不能和-o选项一同使用
--color=auto: 对匹配到的文本着色显示;
-e PATTERN, --regexp=PATTERN:实现以逻辑或的关系完成多个模式的匹配,每个-e选项只能带一个模式作为参数
-E, --extended-regexp:扩展正则表达式开关,能够让grep使用正则表达式
-i, --ignore-case: 忽略字符大小写
-n, --line-number:显示匹配的行在原文件中的行号
-o, --only-matching: 仅显示匹配到的字符串;
-q, --quiet, --silent: 静默模式,不输出任何信息;
-v, --invert-match: 显示那些没有被模式匹配到的行;
-w, --word-regexp:整行匹配整个单词
退出状态:
如果成功查找到被选中的行,状态返回值为0;否则就是1
如果发生了错误,而且没有加-q、--quiet或--silent选项,则状态返回值为2
PATTERN也可称为模式,可以有很多种构成方式:
1.普通的字符或字符串
2.正则表达式(元字符)
例如:
[root@localhost ~]# grep 'root' /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
此例中用于给grep提供匹配条件的PATTERN即为字符串。这样的PATTERN在匹配的时候非常机械,不灵活,所以作用并不明显。
下面针对于常用的基本正则表达式的元字符进行了一个简单的归类,希望对大家能有所帮助:
1.通配符:
.(点) :通用匹配任意单个字符
[]:通用匹配任意指定范围内的单个字符
^ :用在中括号中,对中括号中通配的字符取反,即表示"不包括"的意思.
除此之外,GLOB所提供的通配字符集也是可以在正则表达式中被使用的。下面也列出了常用的几种:
[:digit:]:表示所有的十进制数字,相当于0-9
[:upper:]:表示所有的大写英文字母,相当于A,B,C,D,...,X,Y,Z
[:lower:]:表示所有的小写英文字母,相当于a,b,c,d,...,x,y,z
[:alpha:]:表示所有的英文字母,包括大写和小写,即上两个字符集合的并集。
[:alnum:]:表示所有的英文字母,包括大写和小写字母,还包括所有的十进制数字,前三个字符集合的并集
[:space:]:表示所有的空白字符。
[:punct:]:表示所有的特殊符号,包括,./!?\;'"-=+_等
[^[:punct:]]:表示任意一个所有的非特殊符号的字符。
2.前面字符出现次数:
*:其前面的字符可以出现0次或任意次,相当于:\{0,\}
\?:其前面的字符可以出现0次或1次,相当于:\{0,1\}
\+:其前面的字符至少出现1次,相当于:\{1,\}
\{\}:其前面的字符可以出现任意指定的次数
\{m\}:其前面的字符必须出现m次
\{0,m\}:其前面的字符可以出现0次或最多m次
\{m,\}:其前面的字符至少出现m次
\{m,n\}:其前面的字符至少出现m次,最多出现n次
3.锚定字符:
^ :行首锚定字符
$ :行尾锚定字符
\
\>:词尾锚定字符
4.分组字符:
\(\):将括于括号中的字符分为一组
在正在表达式处理引擎中,如果一旦发现小括号,就按照括号出现的顺序,将对应位置的括号里面的内容记录到某个特定的变量中,而调用这些变量中存储数值的方法,叫做"后向引用",也就是使用\1, \2, \3, ...等
5.选择字符:
\|:或者,表示或者符号左右的整体是可选的内容
如:b\|root,表示b或者root两种情况
\(b\|r\)oot则表示boot或root两种情况,后者是使用\(\)分组的结果。
注意:使用正则表达式元字符的时候,一定要注意以下几点:
1.正则表达式是由正则表达式的元字符构成的,在构成正则表达式的时候,各个元字符之间一般没有空白字符。
2.最好是在引号中使用正则表达式的元字符,否则可能会被shell解释而不是被正则表达式引擎处理了。
3.在书写引号的时候,单引号和双引号都可以,但是要考虑到$字符的使用,一旦有$用来表示行尾,则必须使用单引号,否则将会被shell解释为变量引导符,从而企图将其后面的字符串理解成变量名,从而取到控制,而干扰了我们的使用
4.如果想要使用字符本身,比如".",则需要在这个元字符前面加"\"即可,如"."表示任意单个字符,而'\.'则仅仅只是表示.这个字符本身,有的时候,我们可能要匹配域名或者IP地址,则必须用这个方法才可以。
5.在我们书写正则表达式的时候,应该尽可能周密的思考问题,尽可能把各种情况都考虑进来,从而才能写出最符合要求的正则表达式。
扩展的正则表达式的元字符:
包括了所有的基本正则表达式的元字符,同时有了新的规定:
\? --> ?
\{\} --> {}
\(\) --> ()
\+ --> +
\| --> |
词首锚定和词尾锚定中的"\"依然被保留。
下面我们来做几个例子,以方便大家的理解。
1.取出/etc/sysconfig/network-scripts/ifcfg-eno16777736这个文件的基名[root@localhost ~]# echo /etc/sysconfig/network-scripts/ifcfg-eth0 | grep -o "[^\/]\+\/\?$" | tr -d '/'
ifcfg-eth0
2.找出/etc/rc.d/init.d/functions文件中某单词(包括下划线)后面跟一个小括号的行[root@localhost ~]# grep -o ".*_*.*()" /etc/init.d/functions
systemctl_redirect ()
checkpid()
__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()
3.显示/etc/passwd文件中用户名与该用户的默认shell名相同的用户账户信息[root@localhost ~]# grep -E "(\<.>).*\1$" /etc/passwd
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
4.显示利用ldd命令查看到的ls命令所使用的动态库文件的绝对路径[root@localhost ~]# ldd /bin/ls | egrep -o "/.*lib(64)?/[^[:space:]]+"
/lib64/libselinux.so.1
/lib64/libcap.so.2
/lib64/libacl.so.1
/lib64/libc.so.6
/lib64/libpcre.so.1
/lib64/liblzma.so.5
/lib64/libdl.so.2
/lib64/ld-linux-x86-64.so.2
/lib64/libattr.so.1
/lib64/libpthread.so.0
5.grep还可以从多个文件中查找匹配字符,如:[root@localhost test]# ls
fstab inittab issue
[root@localhost test]# grep 'ro' ./*
./fstab:proc /proc proc defaults 0 0
./inittab:# Ctrl-Alt-Delete is handled by /etc/init/control-alt-delete.conf