第十一章 正则表达式与文件格式化处理

11.1 基础概念

正则表达式是对字符串的处理的表示方式。

11.2 基础正则表达式

11.2.1 语系

采用LANG=C的语系。

特殊符号意义
[:alnum:]英文大小写字符和数字 0-9 a-z A-Z
[:alpha:]英文大小写字符 a-z A-Z
[:blank:]代表空格键和Tab键两种空格符
[:cntrl:]代表 键盘上面的控制按键,包括CR LF Tab Del 等
[:digit:]数字 0-9
[:graph:]除了空格符(空格键、Tab键)之外的其他所有按键
[:lower:]小写字母 a-z
[:upper:]大写字母 A-Z
[:print:]任何可以被打印出来的字符
[:punct:]标点符号
[:space:]任何会产生空白的字符,包括空格键、Tab键、CR等
[:xdigit:]十六进制数字,包括0-9 A-F a-f
grep [-A] [-B] [--color=auto] '查找字符' filename
		-A 后可接数字,为after的意思,除了列出该行之外,后续的n行也跟着列出
		-B 后可接数字,为before的意思,除了列出该行之外,前面的n行也跟着列出


dmesg | grep -n 'tun'				从dmesg输出的所有内容中,将带有关键字tun的那行带行号显示
dmesg | grep -n -A3 -B 2 'tun'		同上,在此基础上,将前面的2行和后面的3行一起显示出来

11.2.3 基础正则表达式练习

调整语系:
export LANG=C
export LC_ALL=C


grep -n 'the' regular_express.txt 				有the的行显示
grep -vn 'the' regular_express.txt 				无the的行显示
grep -ni 'the' regular_express.txt				有不论大小写的the的行都显示
grep -n 't[ae]st' regular_express.txt			有test的和taste的行都会被显示出来,[ae]表示a或者e
grep -n '[^a-z]oo' regular_express.txt 			字符串'oo'前面未出现小写字母的行都显示
grep -n '[^[:lower:]]oo' regular_express.txt 	同上
grep -n '[0-9]' regular_express.txt				有数字的行都显示,
grep -n '[[:digit:]]' regular_express.txt		同上
grep -n '[0-9][0-9]*' regular_express.txt		同上,这是使用正则表达式匹配,上面使用通配符匹配
grep -n '^the' regular_express.txt 				开头是the的行都显示
grep -n '^[a-z]' regular_express.txt 			开头是小写字母的行都显示
grep -n '^[[:lower:]]' regular_express.txt 		同上
grep -n '^[^a-zA-Z]' regular_express.txt		开头别是英文字母的行都显示
grep -n '^[^[:alpha:]]' regular_express.txt		同上
grep -n '\.$' regular_express.txt 				行尾是小数点的行都显示
grep -n '^$' regular_express.txt 				显示空白行,结果是22:
grep -n '^$' regular_express.txt | cat -A		显示空白行,并显示特殊字符,结果是22:$
grep -v '^$' /etc/rsyslog.conf | grep -v '^#'	空白行与开头是井号键的注释行都不显示
grep -n 'g..d' regular_express.txt 				将g与d之间存在两个任意字符的那行显示
grep -n 'oo*' regular_express.txt				第一个o务必存在,后面的o*表示可以有0到无穷多个o
grep -n 'ooo*' regular_express.txt				含有两个或更多连续o的行显示
grep -n 'goo*g' regular_express.txt				字符串首尾都是g,且中间有1个以上的连续o,没有其他杂字符
grep -n 'g.*g' regular_express.txt				字符串首尾都是g就行,不管中间有啥有多少,都显示
grep -n 'o\{2\}' regular_express.txt 			含有两个o的行显示
grep -n 'go\{2,5\}g' regular_express.txt		找到g后面接2-5个o,之后又有个g的行显示
grep -n 'go\{2,\}g' regular_express.txt			找到g后面接2个或2个以上的o,只有又有个g的行显示

^,在字符集合符号(中括号[])内外表示含义不同,在内表示反向选择,在外表示定位在行首。

Windows下的换行符是^M$ ,而Linux下的换行符是$。两者有别。

通配符*,代表任意字符,0个或多个;小数点.代表【一定有一个任意字符】;星号*代表【重复前一个字符,0到无穷多次】,是组合形态。

cat -An regular_express.txt |head -n 10 | tail -n 5		将第5-10行显示出来,带行号,且显示特殊字符
输出:
     6	GNU is free air not free beer.$
     7	Her hair is very beauty.$
     8	I can't finish the test.$
     9	Oh! The soup taste good.$
    10	motorcycle is cheap than car.$

11.2.4 基础正则表达式字符集合

RE字符意义
^word带查找的字符串word在行首
word$带查找的字符串word在行尾
小数点.一定有一个任意字符
\转义符,将特殊符号的特殊意义去除
*重复0个到无穷多个的签一个RE字符
[list]字符集合的RE字符,里面列出想要选取的字符
[n1-n2]字符集合的RE字符,里面列出想要选取的字符范围
[^list]字符集合的RE字符,里面列出不要的字符串或范围
{n,m}连续n到m个的前一个RE字符;{n}是连续n个的前一个RE字符;{n,}连续n个以上的前一个RE字符
ls -l *					匹配任意文件
ls -l a*				匹配文件名开头是a的文件
ls | grep -n '^a.*'		匹配文件名开头是a的文件
ls -l /etc | grep '^l'	找到/etc下的链接文件【lrwxrwxrwx】开头是l
ls -l /etc | grep '^l'|wc -l	这样的文件总共有多少个,列出个数

11.2.5 sed工具

sed本身也是管道命令,可以分析标准输入。可以完成替换、删除、新增、选取特定行等功能。

sed [-nefr] [操作]
		-n  安静模式,只有经过sed处理的行才显示到屏幕上
		-e  直接在命令行模式上进行sed的操作编辑
		-f  直接将sed的操作写在一个文件内,-f filename 可以执行filename内的sed操作
		-r  sed的操作使用的是扩展性正则表达式语法(默认是基础正则表达式语法)
		-i  直接修改读取的文件内容,而不是由屏幕输出
操作:[n1[,n2]] function
		n1,n2  未必存在,选择进行操作的行数
		function包括: 	a  新增
						c  替换
						d  删除
						i  插入
						p  打印
						s  替换		
nl /etc/passwd | sed '2,5d'		删除2-5行后的内容显示在屏幕上
nl /etc/passwd | sed '2d'		删除第2行后的内容显示在屏幕上
nl /etc/passwd | sed '3,$d'		删除第3行到最后一行之后的内容显示在屏幕上
nl /etc/passwd | sed '/^$/d'	删除空白行



nl /etc/passwd | sed '2a drink tea' 即在第二行结束,第三行开头添加drink tea字样
nl /etc/passwd | sed '2i drink tea' 即在第二行前面,第一行后面添加drink tea字样

可以新增好几行数据,必须用转义字符+Enter键隔开:
nl /etc/passwd | sed '2a drink tea or \
> drink bear?'



替换整行:
nl /etc/passwd | sed '2,5c No 2-5 number'		将第2-5行数据替换成No 2-5 number
输出:
     1	root:x:0:0:root:/root:/bin/bash
No 2-5 number
     6	sync:x:5:0:sync:/sbin:/bin/sync



按行数输出整行内容:
nl /etc/passwd | sed -n '5,7p'



以行为单位,实现部分数据的查找和替换:
sed 's/要被替换的字符/新的字符/g'
sed 's/要被替换的字符//g'		将匹配到的字符串删除
cat /etc/man_db.conf | grep 'MAN'|sed 's/#.*$//g'	删除掉每一行注释之后的内容,原本的注释行都变成了空白行
cat /etc/man_db.conf | grep 'MAN'|sed 's/#.*$//g' | sed '/^$/d'	因此还得删除空白行



直接修改文件,将文件末尾的点换成感叹号:
sed -i 's/\.$/\!/g' regular_express.txt 
直接修改文件,在文件末尾添加一句注释# This is a test
sed -i '$a # This is a test' regular_express.txt

11.3 扩展正则表达式

RE字符意义
+重复1个或1个以上的前一个RE字符
?0个或1个的前一个RE字符
|用或(or)的方式找出数个字符串
()找出群组字符串
()+多个重复群组的判别

11.4 文件的格式化与相关处理

11.4.1 格式化打印:printf

printf '打印格式' 实际内容
		\a		警告声音输出
		\b		退格键(backspace)
		\f		清屏
		\n		输出新行
		\r		回车按键
		\t		水平的Tab键
		\v		垂直的Tab键
		\xNN   	NN是两位数数字,可以转换数字成为字符
		%ns  	多少个字符
		%ni	 	多少个整数位数
		%N.nf  	共要N个位数,其中小数点占n位
		
printf '%s\t %s\t %s\t %s\t \n' $(cat printf.txt) 	要先把文件中的内容提取出来给printf用
printf '\x45\n'		看十六进制的数值45代表的字符是啥

11.4.2 awk:好用的数据处理工具

awk比较倾向于一行当中分成数个字段来处理,适合处理小型文本数据。

awk '条件类型1{操作1} 条件类型2{操作2} ...'  filename   默认分隔符是空格键和Tab键

last -n 5 | awk '{print $1 "\t" $3}'				 显示前5行中的第一字段和第三字段(awk最常用的功能)

last -n 5 | awk '{print $1 "\t lines:" NR "\t columns:" NF}'	输出字段1,目前处理的第几行NR,该行有几个字段NF

cat /etc/passwd | awk '{FS=":"} $3 < 10 {print $1 "\t" $3}'		将第3字段的UID号码小于10的那行的字段1和字段3显示出来(这种方式,第一行数据无法正确处理,那个时候的分隔符还是默认为空格)

cat /etc/passwd | awk 'BEGIN {FS=":"} $3 < 10 {print $1 "\t" $3}' 将第3字段的UID号码小于10的那行的字段1和字段3显示出来(解决上述问题)
变量名称意义
NF每一行($0)拥有的字段总数
NR目前awk所处理的是第几行数据
FS目前的分隔字符,默认是空格键

11.4.3 文本比对工具

diff通常用在同一个文件(或软件)的新旧版本差异上,以为单位比对。不要用diff比较两个不相关的文件。

diff [-bBi] from-file to-file
		from-file  	文件名,作为原始比对文件的文件名
		to-file   	文件名,作为目标比对文件的文件名
		from-file和to-file可以用-来替换,-此处代表标准输入
		-b			忽略一行当中,仅有多个空白的差异,认为“a a”与“a    a”相同
		-B			忽略空白行的差异
		-i			忽略大小写的不同



mkdir -p /tmp/testpw
cd /tmp/testpw/
cp /etc/passwd passwd.old
cat /etc/passwd | sed -e '4d' -e '6c no six line'>passwd.new
diff passwd.old passwd.new 
输出:						
4d3											左边第4行被删除了,基准是右边的第三行
< adm:x:3:4:adm:/var/adm:/sbin/nologin		列出了左边文件被删除的那一行内容
6c5											左边文件的第6行被替换成右边文件的第5行
< sync:x:5:0:sync:/sbin:/bin/sync			左边文件第6行
---
> no six line								右边文件第5行




diff /etc/rc0.d/ /etc/rc5.d/				运行级别0和运行级别5的启动脚本文件对比
输出:
只在 /etc/rc0.d/ 存在:K90network
只在 /etc/rc5.d/ 存在:S10network
cmp [-l] file1 file2
		-l  将所有不同点的字节处都列出来,默认仅输出第一个发现的不同点


cmp passwd.old passwd.new 	
输出:
passwd.old passwd.new 不同:第 106 字节,第 4 行
diff -Naur passwd.old passwd.new > passwd.patch		制作补丁文件
cat passwd.patch 
输出:
--- passwd.old	2020-06-07 16:02:19.455870231 +0800
+++ passwd.new	2020-06-07 16:03:01.811869325 +0800
@@ -1,9 +1,8 @@
 root:x:0:0:root:/root:/bin/bash
 bin:x:1:1:bin:/bin:/sbin/nologin
 daemon:x:2:2:daemon:/sbin:/sbin/nologin
-adm:x:3:4:adm:/var/adm:/sbin/nologin
 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
-sync:x:5:0:sync:/sbin:/bin/sync
+no six line
 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
 halt:x:7:0:halt:/sbin:/sbin/halt
 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin

需要安装软件才能使用:
patch -pN < patch_file
patch -R -pN < patch_file

patch -p0 < passwd.patch	用刚刚制作出来的patch文件更新旧文件
patch -R -p0 <passwd.patch  恢复旧文件

11.4.4 文件打印设置:pr

打印的时候,打印标题和页码的设置。

pr /etc/man_db.conf

11.6 习题

grep '\*' /etc/* 2>/dev/null					找到/etc文件夹下的带*内容

grep '\*' $(find /etc -type f) 2>/dev/null		找到/etc包括子文件夹下的文件的带*内容

grep -l '\*' $(find /etc -type f) 2>/dev/null	找到符合的文件名就行,不用找内容



Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐