1. 文本处理—{grep}
- grep:文本过滤(模式:pattern)工具
- grep, egrep, fgrep(fgrep不支持正则表达式搜索)
- grep: Global search REgular expression and Print out the line
- 作用:文本搜索工具,根据用户指定的“模式”对目标文本逐行进行匹配检查;打印匹配到的行
- 模式:由正则表达式字符及文本字符所编写的过滤条件
- grep 需要标准输入,后面不加file的话则是对标准输入内容进行过滤
grep [OPTIONS] PATTERN [FILE...]
grep root /etc/passwd
参数扩展,不扩展,命令替换的例子:
grep "$USER" /etc/passwd:双引号可以识别变量
grep '$USER' /etc/passwd :单引号会把里面内容全部当做字符
grep \`whoami\` /etc/passwd :反向单引号则可以识别命令和变量
注意点1:grep处理命令的时候是按照一行一行处理,每次处理一行存入它自己的内存空间中,处理完之后存入下一行继续处理直到结束.
命令选项:
--color=auto: 对匹配到的文本着色显示
-m # 结果中匹配到#次(行)后停止:
注意1:-m 代表max counts,这个是结果最多匹配到几次(行)之后停止匹配的意思,而不是匹配file文件的前几行;
注意2:同时还有一点就是这个匹配次数是按照匹配到的行数来算的,也就是说如果一行内多次匹配到partern的字符,但因为在同一行内,只计算一次count,算匹配到一次;
注意3:由此也可见grep就是一行一行进行匹配的;
-v 显示不被pattern匹配到的行
-i 忽略字符大小写
-n 显示匹配的行号
-c 统计匹配到的行数:显示匹配到pattern的行的总数
-o 仅显示匹配到的字符串,并把每一个结果分一行输出显示:因为并不是固定的,所以还是很有意义的
-q 静默模式,不输出任何信息 :echo $? 找到显示为0 找不到显示为1,返回值不同
-A # after, 显示找到的行外加后#行:可用于想要找的行没有关键字但是相邻行有关键字的时候进行匹配,更加容易,下面两同样如此。
-B # before, 显示找到的行外加前#行
-C # context, 显示找到的行外加前后各#行
-e partern 匹配partern;并且实现多个partern间的逻辑or关系
例如:grep –e 'cat' -e 'dog' file
注:如果想实现与的关系,则用管道进行多次过滤即可;并没有特定的与的选项;
-f file 根据模式文件处理:根据文件里面写的pattern来过滤,每一行都是一个pattern,每一行的parteen是或的关系(相当于每一行都是-e)
-w 匹配整个单词:pattern前后不能带字母,数字或者下划线(它认为数字和下划线也是单词)
-E 使用ERE:扩展的正则表达式
-F 相当于fgrep,不支持正则表达式
2. 正则表达式
- REGEXP: Regular Expressions,由一类特殊字符及文本字符所编写的模式,其中有些字符(元字符)不表示字符字面意义,而表示控制或通配的功能
- 程序支持:grep,sed,awk,vim, less,nginx,varnish等
- 分两类:
- 基本正则表达式:BRE
- 扩展正则表达式:ERE grep -E, egrep
- 正则表达式引擎:
- 采用不同算法,检查处理正则表达式的软件模块
- PCRE(Perl Compatible Regular Expressions)
- 元字符分类:字符匹配、匹配次数、位置锚定、分组
- man 7 regex
1.字符匹配:
. 匹配任意单个字符 ,经测试注意这里的任意包括不可见的非打印字符
[] 匹配指定范围内的任意单个字符,示例:[wang] [0-9] [a-z] [a-zA-Z]
[^] 匹配指定范围外的任意单个字符
[:alnum:] 字母和数字
[:alpha:] 代表任何英文大小写字符,亦即 A-Z, a-z
[:lower:] 小写字母
[:upper:] 大写字母
[:blank:] 空白字符(空格和制表符Tab)
[:space:] 水平和垂直的空白字符(比[:blank:]包含的范围广)
[:cntrl:] 不可打印的控制字符(退格、删除、警铃...)
[:digit:] 十进制数字
[:xdigit:]十六进制数字
[:graph:] 可打印的非空白字符
[:print:] 可打印字符(包括空白字符等,比graph更广)
[:punct:] 标点符号
2.匹配次数
- 用在要指定次数的单个字符后面,用于指定前面的字符要出现的次数
- 如果前面字符添加了分组(小括号),则此时可以对前面的整个分组的次数进行匹配,否则只是单个字符
- 比如 abc\{3\} 和\(abc\)\{3\} 的区别就是前者匹配c3次,而后者匹配abc三次
* 匹配前面的字符任意次,包括0次 贪婪模式:尽可能长的匹配 .* 任意长度的任意字符(可以匹配“什么都没有”) \? 匹配其前面的字符0或1次 \+ 匹配其前面的字符至少1次,相当于\{1,\} \{n\} 精确匹配前面的字符n次 \{m,n\} 匹配前面的字符至少m次,至多n次 \{,n\} 匹配前面的字符至多n次 \{n,\} 匹配前面的字符至少n次
注意: grep "\?" (主要是*和?这两个可以代表0次的匹配)
- 其实它能匹配所有的行
- 因为\?代表它前面的字符的0次或者1次,它前面只要写任意的一个字符,包括空格什么的都行;
- 在grep匹配的时候,就算每一行不能匹配到它前面字符的1次,但也能匹配到它前面字符的0次呀;这也就相当于这一行仍旧被匹配到了!
- 这种看起来好像并没有匹配任何结果,然而实际上匹配到了0次,符合它的要求;逻辑上算是匹配到了结果的;用
echo $?可知结果是0; - 只不过是它不会显示红色匹配到的字符而已,但所有的行都会算到匹配结果中并输出
3.位置锚定:定位出现的位置
^ 行首锚定,用于REGEXP的最左侧
$ 行尾锚定,用于REGEXP的最右侧
^REGEXP$ ,用于REGEXP匹配整行
^$ 空行
^[[:space:]]*$ 含有空白的行(那些看起来像是空行,但其实包含可打印字符:空格,tab,等等空白字符的行)
\< 或 \b 词首锚定,用于单词的左侧
\> 或 \b 词尾锚定,用于单词的右侧
\<PATTERN\> 匹配整个单词 (在grep中则和-w选项意义相同)
4.分组详解
- 分组:\(\) 将一个或多个字符捆绑在一起,当作一个整体处理,如:\(root\)\+
- 分组括号中的partern匹配到的内容会被正则表达式引擎记录于内部的变量中,这些变量的命名方式为: \1, \2, \3, …
- \1 表示从左侧起第一个左括号以及与之匹配右括号之间的模式所匹配到的字符
- \2 则表示从左侧起第二个左括号以及与之匹配右括号之间的模式所匹配到的字符
- 之后的都是这样的规律
- 示例:
- \(string1\+\(string2\)*\)
- \1 :string1\+\(string2\)*
- \2 :string2
后向引用:引用前面的分组括号中的模式所匹配到的字符,而非模式本身
- 这里也就是说,引用的内容是前面已经匹配到的结果的确定的字符,而并非是模式了
- 有点像波函数坍缩的过程,比如前面的partern匹配到了结果,则这个partern就坍缩了,变成了确定的内容;然后这个后向引用引用的就是这个确定的结果。
分组举例:
# 1.分组用于次数
grep "abc\{3\}" :这是c出现了3次相当于 abccc
grep "\(abc\)\{3\}" :abc出现了三次 相当于abcabcabc (分组的第一种应用,没有使用后向引用)
# 2.分组使用后向引用
这里我用中括号代表被匹配到的字符,便于查看区别:
# echo root radt root rawd |grep "\(r..t\).*\1"
[root radt root ]rawd
# echo roXt radt root radtwd |grep "\(r..t\).*\1"
roXt [radt root radt]wd
假如不用后向引用则能全部匹配到,分组也就没有意义了:
# echo root radt root rawd |grep "\(r..t\).*"
[root radt root rawd]
### 仔细查看前面两个之前的区别;以及前面两个和后面一个的区别(最大匹配原则和精确匹配);自行体会一下
5.或者or:\|
示例:
a\|b: a或bC\|cat: C或cat\(C\|c\)at:Cat或cat
例如:
grep "^\(r\|b\)":以r或者b开头的行,注意要把它括起来作为一个整体- 同理:
grep "abc\|d":表示abc或者d grep "ab\(c\|d\)":表示abc或者abd
- 同理:
注意: 这里的括号是用来规定或符号
\|的范围的,括号的地方不是括的(ab)c\|d,而是一定要把这个或\|字符的部分给括进去,这样才能规定范围,注意别括错了括到外面去了。如果不加括号,则会把或
\|字符的左右两边分别每一边的所有的字符看做一个整体进行或的匹配,这点要注意。
3. 注意点1:
- 通配符是处理文件名(pathname expansion)的时候使用的;正则表达式匹配文件内容的
- 但也可以用
ls|grep ....的方式,把文件名当做字符串输出,然后用正则表达式进行处理
- 但也可以用
- 通配符和正则表达式容易混淆的符号等:
*:通配符中它本身代表为任意长度任意字符(包括0次);正则表达式中则是匹配它前面的字符的0次或任意多次,具有贪婪模式.:通配符中没有意义 ,正则表达式中代表任意的一个字符?:通配符中它本身代表一个任意字符(类似正则表达式的.),正则表达式中则是匹配它前面的字符0次或者1次[]:中括号内的内容在通配符和正则表达式中都是代表匹配其中任以一个字符,但是注意通配符中的a-zA-Z等等,是按照小写大写小写大写的顺序;而正则表达式中的a-z和A-Z分别就是代表小写字母和大写字母,同样的tr里面的也是仅仅代表着小写和大写;花括号扩展{a..z}中的也是只代表小写或者大写的范围,因此只有通配符的用-表示英文字母范围的时候是小写大写混用的。
^和[^]在正则表达式中分别代表行首铆定和指定范围外,别混淆了- 在正则表达式中系统定义的字符匹配同样只是代表一个字符,因此要在外面再加上中括号比如
[[:upper:]],和通配符一样 ,但是tr中不需要再加上[],它本身就代表一个。
- 注意
.必须代表一个字符,不能匹配什么都没有;和下面4要区分开 .*和\?都可以匹配到“什么都没有”;因为*和\?都可匹配0个;- 在逻辑上它其实是匹配到了结果的,
echo $?的结果是0但是看起来好像什么都没有匹配到(不显示红色) - 注意“什么都没有”和“不可打印字符 空字符MUL”并不是同一回事;
- 空字符也是一个字符,能被.匹配;只不过它不能显示出来罢了;匹配结果会显示能匹配到但是结果打印不出来。
- 在逻辑上它其实是匹配到了结果的,
- 注意当用
grep的-o选项的时候,它会把每一个匹配到的结果单独一行输出;- 即匹配的字符如果在一行中有多个匹配,但是这多个匹配之前有分隔(或者说没有分隔但每一个字符都是一个匹配);用
-o选项结果中会每行显示一个结果分行显示,如下面的例子 - 同时也要注意最大贪婪匹配的规则,也就是会把最大能匹配到的看做是一次匹配成功:例如
#### 分行输出 # echo abcd |grep -Eo "." a b c d #### 最大贪婪匹配 # echo abcd |grep -Eo ".*" abcd # echo abcd |grep -Eo ".?" a b c d # echo abcd |grep -Eo ".+" abcd #### 匹配顺序和匹配重复机制 echo abcdefg |grep -Eo ".{1}" a b c d e f g # echo abcdefg |grep -Eo ".{2}" ab cd ef # echo abcdefg |grep -Eo ".{3}" abc def # echo abcdefg |grep -Eo ".{4}" abcd # echo abcdefg |grep -Eo ".{5}" abcde # echo abcdefg |grep -Eo ".{6}" abcdef 注意,从这个例子也可看出,grep匹配的顺序是每一行从左往右进行,同时已经被匹配到字符,不能被再次被匹配到了。 比如上面的第二个结果不能是" ab bc cd.." 这样的, 同时第5个结果也不能是"abcde bcdef ..." 这样的。 具体更多的用法多尝试和自行体会。 - 即匹配的字符如果在一行中有多个匹配,但是这多个匹配之前有分隔(或者说没有分隔但每一个字符都是一个匹配);用
4. grep结合正则表达式举例子:
1. 匹配以非#开头的行(下面两个命令的区别需要注意:)
grep -Ev "^#" file :
1.它的匹配逻辑是先找到以#字符开头的行,然后取它的反,也就是匹配除了以#开头之外的其他的行
2.这样的话,就算是“什么都没有”的行(^$),也会被匹配到并输出到结果中;因为空行也不是以#开头的行;
grep -E "^[^#]" file :
1.它的匹配逻辑是,匹配除了#外的任意一个字符开头的行;也就是说要被匹配到的行开头必须有任意的一个字符
2.而这任意的一个字符,既可以是打印字符也可以是非打印字符;
3.其中打印字符包括 "空格 横向tab(^I) 垂直tab等等",可以用cat -A 查看全部的字符;
4.非打印字符则包括 "回车(^M) 换行(^@) 空NUL(^@)" 等等
5.注意,如果用cat -A 命令查看文件,文件的某一行的开头就是$符号(行尾锚定符号),也就代表这一行是空行,也就是“什么都没有”的行;这个$符号并不被认为是一个字符,它只代表行尾的意思;
6.这一“什么也没有”的行就是啥都没有的意思,不含任何打印或者非打印字符;也就是说这一行既没有“空格 tab”等等可打印字符(直接看起来好像是空行,但用cat -A可看到并不是),也没有什么 “换行 回车”等等这种非打印字符;它就代表什么都没有的行!!
7.因此,由于这个命令要匹配到除了#意外的任意一个字符开头,所以说这种“什么都没有”的空行(行的开头就是$行尾符)也就不会被匹配到了
grep -Ev "^([[:space:]]*#|$)" file
去除“什么都没有的”空行,#开头的行,以及先以空格开头后跟#的行;这个最通用。
grep -Ev "^([[:space:]]*#)" file
或者用这个也可,把“什么都没有的空行”保留,只去除注释行,这个命令让结果看起来更舒适,保留了空行,但是建议保留空行之后,对结果再进行一次uniq,这样的话多个连续的空行就会被剔除,结果显示的更加舒适:如下
grep -Ev "^([[:space:]]*#)" file | uniq
总结:
1.第一个命令可以匹配到非#字符开头的其他所有行;包括 可以匹配到(1)空行,(2)以“空格 SPACE”这种开头的看起来像是空行的行(3)非打印字符开头的看起来像是空行的行(4)以“空格 SPACE”这种开头但后面仍有#的注释行
2.后面一个命令可以匹配到以任意一个非#的其他任意字符(打印和非打印)开头的行;它不能匹配到(!!)空行,但是可以匹配到(2)以“空格 SPACE”这种开头的看起来像是空行的行,以及(3)以各种非打印字符开头的看起来像是空行的行
3. 最后一个命令
2.匹配文件的空行:
grep "^$" file
3.匹配文件中的非空行:
grep -v "^$" file
4.查找就是这个word的字符,它前面后面不能有其他非单词的字符,不能多也不能少
grep "\<word\>" file
或 grep -w "word" file
5.去掉文件中#开头的行和空行(用了扩展的正则表达式),只匹配其他行(在脚本中一般是生效代码行)
grep -Ev "^#|^$" file
或 grep -Ev "^(#|$)" file
或 grep -Ev -e "^#" -e "^$" file
或 grep -E "^[^#]" file
注意它们都不能去掉包含 可打印字符“空格 tab space”等等开头的行,这样结果就看起来好像还是有“什么都没有”的空行,其实结果中已经不存在空行了,看起来的空行其实含的有这些“空格”等等字符。
6. 去除centos小版本号,只查看其大版本号码:
# cat /etc/system-release | grep -Eo "([0-9]+\.){2}[0-9]+"
7.6.1810
# cat /etc/centos-release |grep -oE "[0-9]+"
7
6
1810
# cat /etc/centos-release |grep -oE [0-9]+| head -1
7
#### 或者(注意数字前有空格):
# cat /etc/centos-release |grep -oE " [0-9]+" |tr -d " "
7
#### 或者:
# cat /etc/centos-release |grep -oE "[0-9]+" |grep -m 1 ".*"
7
7. 取出磁盘占用率(去掉各种内存文件的占用率等等)
# df| grep "^/dev/sd"|grep "[0-9]\{1,3\}%" -o |grep "[0-9]\+" -o
4
1
18
### 或者
# df | grep "^/dev/sd" | grep "[[:digit:]]*%" -o |grep "[0-9]*" -o
4
1
18
### 也可用之前的cut命令,多种方法都可用
5. 扩展的正则表达式—{egrep}
6. egrep
egrep = grep -E
egrep [OPTIONS] PATTERN [FILE...]
1.字符匹配:
. 任意单个字符
[] 指定范围的字符
[^] 不在指定范围的字符
2.次数匹配:
* 匹配前面字符任意次
? 0或1次
+ 1次或多次
{m} 匹配m次
{m,n} 至少m,至多n次
3.位置锚定:
^ 行首
$ 行尾
\<, \b 语首
\>, \b 语尾
4.分组:
()
后向引用:\1, \2, ...
5.或or
a|b a或b
C|cat C或cat
(C|c)at Cat或cat
7. egrep举例子
- 取IP
## 命令
ifconfig |grep -E "([0-9]{1,3}\.){3}[0-9]{1,3}"
ifconfig eth0 |grep -Eo "([0-9]{1,3}\.){3}[0-9]{1,3}" |head 1
----------------------------------------
### 下面是中间过程的查看
20:23[root@centos7 /data]# ifconfig |grep -E "([0-9]{1,3}\.){3}[0-9]{1,3}"
inet 192.168.36.102 netmask 255.255.255.0 broadcast 192.168.36.255 inet 127.0.0.1 netmask 255.0.0.0
inet 192.168.122.1 netmask 255.255.255.0 broadcast 192.168.122.255
20:34[root@centos7 /data]# ifconfig ens33 |grep -Eo "([0-9]{1,3}\.){3}[0-9]{1,3}"
192.168.36.102
255.255.255.0
192.168.36.255
- 找出passwd用户名和shell相同的行:
13:37[root@centos7 /data]# useradd sh
13:38[root@centos7 /data]# getent passwd sh
sh:x:1005:1007::/home/sh:/bin/bash
13:38[root@centos7 /data]# egrep "^(.*):.*/\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
或者:
13:38[root@centos7 /data]# egrep "^(.*):.*\<\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
### 注意这里后项引用之后要么前面加一个/,要么就单词锚定
- 取基名和目录名:
13:31[root@centos7 /data]# echo "/etc/rc.d/init.d/functions" |egrep "^[/].*/" -o
/etc/rc.d/init.d/
13:31[root@centos7 /data]# echo "/etc/rc.d/init.d/functions" |egrep "^/.*/" -o
/etc/rc.d/init.d/
13:31[root@centos7 /data]# echo "/etc/rc.d/init.d/functions" |egrep "[^/]*$" -o
functions
- 找出/etc/passwd中两位或三位的数
egrep "\<[0-9]{2,3}\>" /etc/passwd
或者 egrep -w "[0-9]{2,3}" /etc/passwd
## 注意如果这里一定要加上-w或者词首词尾锚定,
不然的话 大于4位数的数(5位数包括以上)也会被匹配到(被拆分开匹配到)
- 比如 4位数会匹配到前3位(最大匹配原则,不会说拆成2个2位数)
- 5位数会被拆成一个3位数(在前)和一个2位数(在后) 然后被匹配到
留言