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

最后更新于 2021-10-08 498 次阅读


1.正则表达式

(1)什么是正则表达式?

我们通过上一节的学习,了解了BASH中通配符的用法,在这一节中,我们将要学习正则表达式的用法。

那么,什么是正则表达式?

正则表达式书上标准的概念为:正则表达式就是一种处理字符串的方法,通过一些特殊符号的帮助,以行为单位来进行字符串的处理工作。

通过使用正则表达式,我们可以灵活的进行一些数据信息的筛选和查找,正则表达式也便于我们在使用一些软件或脚本时,对一些字符的输入输出进行筛选。

在学习正则表达式之前,我们还要搞清楚正则表达式和之前我们所学的通配符的区别。

正则表达式与通配符的关键区别在于,正则表达式是一种字符串处理的表达方式,而之前我们所学习的通配符是BASH操作接口的一个功能,正则表达式基本就是一种【表示方法】,只要程序支持这种表示法,那么该程序就可以用来作为正则表达式的字符串处理之用。

(2)语系的选择与正则表达式

① 语系(LANG)对正则表达式的影响

在开始正则表达式的学习前,我们要弄清楚,既然正则表达式是一个处理字符串的表示方式,那么语系对于字符排序的影响就势必会影响到我们使用正则表达式的结果。

由于不同语系的编码数据不相同,所以会造成数据选取结果的差异。

例如在语系LANG=C中,英文大小写和数字的编码排序是:0 1 2 3 4 … A B C D … Z a b c d …z

而在中文语系中LANG=zh_CN中,编码排序是:0 1 2 3 4 … a A b B c D … z Z

这两者的差异就导致了在我们想查找小写字母而使用表达式【a-z】时,LANG=C确实可以识别出a-z的小写字母(因为连续),而LANG=zh_CN却会连A-Z也一起识别出来。

所以我们在使用正则表达式时,一定要注意当前的语系环境,以免出现和别人不同的选择结果。

② 进行字符选取时的一些常用特殊字符

为了避免在选取英文和数字字符时,编码语系对我们造成的影响,我们需要了解一些常用的特殊字符。

Ⅰ.[[:alnum:]]

代表英文大小写字符及数字,即a-z,A-Z,0-9

Ⅱ.[[:alpha:]]

代表英文大小写字符,a-z,A-Z

Ⅲ.[[:digit:]]

代表数字字符,0-9

Ⅳ.[[:lower:]]

代表小写字符,a-z

Ⅴ.[[:upper:]]

代表大写字符,A-Z

Ⅵ.[[:blank:]]

代表空格与【TAB】键两者

Ⅶ.[[:punct:]]

代表标点符号,即" ' ? ! ; : # $等

(3)基础正则表达式字符(RE字符)集合

经过总结,我们常用的基础正则表达式字符有如下这些:

① “^word”:行首关键词字符

之前我们在学习通配符的时候,知道了"^"在"[^]"里的时候代表的是反选的意思,但是在正则表达式中,"^"符号在括号“[]”以外表达的意思是:待查找的字符串在行首

范例:grep '^ init.*' 查找以init开头的行

grep ^行首符号演示

② “word$”:行尾关键词字符

既然有上边的行首关键词,那么肯定有一个行尾关键词,就是“$”。意义:待查找的字符串在行尾

范例:grep -n ‘!$’ regular_express.txt 查找以!结尾的那一行显示出来,并打印行号。

grep $符号演示

③ “.”:代表一个任意字符

意义:代表一定有【一个】任意字符

"."的用法其实和通配符里的“*”有点类似,只不过通配符里的"*"代表的是【0个或无穷个任意字符】,而正则表达式“.”则代表了一定有【一个】任意字符(空格也算),注意这个【一个】。

范例:grep ‘e.e’ pay.txt

查找e开头和e结尾的一个三个字符的字符串(可以是eve,也可以是ege,更可以是e e,但是绝不可能是ee )

④ “*”:代表重复前一个RE字符

意义:重复前一个RE字符0次或无限次

一定要区分正则表达式里的“*”和通配符里的“*”,正则表达式里的“*”常常紧跟着另一个RE字符使用,例如【.*】就代表着一个任意字符。

范例:例如我想查找一个以g开头,以g结尾,中间带着两个或两个以上的o的单词,我应该怎么输入?

这时候的命令应该是:grep 'gooo*g' require_express.txt

由于"*"前边的字符o可以重复0到无穷次,所以在重复0次时,查找的就是"good",重复1次是“goood”....以此类推,满足要求

grep *符号演示

⑤ “\”:转义符

意义:转义符,将符号的特殊意义去掉

范例:grep '\\\`' pay.txt

查找单引号“`”存在的那一行

⑥ “[list]”

意义:字符集合的RE字符,里边列出想要选取的字符并从中选择【一个】

范例:grep ‘g[apc]f’ pay.txt

在pay.txt中寻找gaf、gpf和gcf三个字符,总之[apc]就代表着要不是a、p、或者c的意思

⑦ “[n1-n2]”

意义:字符集合的RE字符,里面列出想要选取的字符范围并从中选出【一个】

要注意,“-”号代表着一个连续的字符范围,这个和当前语系的编码有着紧密联系,例如大写字母就为[A-Z]。

范例:grep -n '[A-Z]' pay.txt

⑧ “[^word]和”[^n1-n2]

意义:^在[]中括号内,代表着反选的意思,除了给定的word以外都行

范例:grep -n '[^A-Z]' pay.txt

在pay.txt中寻找不存在大写字母的行

⑨ “\{n,m\}”

意义:用于限定 “\{n,m\}”之前的字符串的重复次数,表示之前的字符串重复n到m次。

若是\{n\},则为之前的字符串重复n次

若是\{n.\},则为之前的字符串重复n次以上

范例:grep -n 'go\{1,3\}d' pay.txt

在pay.txt寻找o存在1到3次的字符,即查找[god]和[good]还有【goood】

(4)扩展正则表达式

除了标准的正则表达式以外,linux还提供一些扩展的正则表达式以便简化我们的命令操作

以下所介绍的扩展正则表达式无法直接的在grep命令中使用,因为grep命令默认只支持基础正则表达式,如果我们想要让grep命令运行扩展正则表达式,就使用【egrep】或【grep -E】命令

① “+”

意义:重复【一个或一个以上】的前一个RE字符

范例:egrep -n ‘go+d’ regular_express.txt

在文件中查找god、good、goood等字符串,其实这个“o+”的扩展正则表达式和基础正则表达式的“oo*”的显示结果是一致的

egrep +符号演示

② “?”

意义:重复【0个或一个】的前一个RE字符

范例:egrep -n ‘go?d’ regular_express.txt

在文件中查找gd和god两个字符串

egrep ?符号演示

③ “|”

意义:用或(or)的方式找出多个字符串

范例:egrep -n ‘god|good|dog’ regular_express.txt

在文件中查找god或good或dog等字符串存在的行

egrep |命令演示

④ “( )”

意义:找出【群组】字符串

范例:egrep -n ‘g(la|oo)d’ regular_express.txt

我如果想在文件中查找glad和good这两个字符串,由于这两个字符串有g和d重复,所以我们就可以将“()”和"|"结合使用

egrep ( )符号演示

⑤ “( ) +”

意义:多个重复群组的判别

范例:echo ‘GxyzxyzxyzD’ |egrep -n 'G(xyz)+D'

假设我想查找GxyzxyzxyzD这个字符串,因为中间有个重复的xyz,所以我可以使用(xyz)+这个扩展正则表达式来进行表示

egrep ( )+符号命令演示

2.文件格式化处理

(1)sed命令:行编辑器

sed命令又称流编辑器、行编辑器,其作用是将每行内容读入到内存中,在内存中进行处理,将结果返回给屏幕 ,此段内存空间称为模式空间默认不编辑原文件,仅对模式空间的数据进行处理,处理结束后,将模式空间的内容显示到屏幕。

sed命令的用途十分广泛,配合正则表达式能够让我们进行数据的增加、删除、替换、选定特定的行等功能。其本身也是一个管道命令,能够对前个命令的标准输出进行数据处理

① 选项及参数

命令格式:sed 【-n e f r i】 【操作】

常用参数:

-n:使用安静模式(silent)进行输出,在一般的sed命令使用中,所有来自stdin的数据一般都会列出到屏幕上(被sed处理过后的数据会重复显示),但是使用了-n参数后,只有被sed处理过后的数据会显示在屏幕上。

sed默认模式和安静模式对比

-e:同时执行多个匹配操作

例如:nl /etc/fstab |sed -e '2,5d' -e '6a like this'

命令解释:打印/etc/fstab的内容,并删除2到5行的内容,并在第6行的前边添加‘like this’这句话

sed -e参数演示

-f:-f参数后边一般接写好的脚本文件,脚本文件里是希望sed执行的多个命令

例如:sed -f /tmp/sedfile.txt /etc/fstab

-r:让sed使用扩展型正则表达式的语法(默认sed使用基本正则表达式的语法)

-i:让sed直接能够修改文件的内容,而不是只是进行屏幕输出(危险命令)

② 操作说明

【[function] n1[,n2] [function]】

一般sed命令后都会接上你想要进行的操作,其中n1,n2代表着【想要进行操作的行数】,有时不一定会存在,假设我是想在第5和10行之间进行操作,那我的操作命令就可以写成‘5,10[function]'

[function]具体有以下的参数:

a:新增,a的后续可以接字符,代表在第a行的前边新增这些字符

例如:nl /etc/fstab |sed '3a asoul diana'

命令解释:在/etc/passwd这个文件内原先第三行的地方添加字符‘asoul diana’

sed function a参数演示1

如果想新增多行,只需要这样:nl /etc/passwd |sed '3a asoul diana \\ ava xiangwan,每新增一行,就多使用一个“\”

sed function a参数演示2

i:插入,和a参数差不多,后续也可以接字符,但是i插入的字符是在你选择的行的上一行进行插入(区别于a参数的下一行插入)

sed function i参数演示1

d:删除,可以删除我们指定的行。

例1:nl /etc/passwd |sed '5,15d'

命令解释:删除/etc/passwd文件的第五到第15行

sed function d参数演示1

例2:nl /etc/passwd |sed '/^$/d'

命令解释:删除空白行

例3:nl /etc/passwd |sed '/root/d'

命令解释:删除含有字符‘root’的行,记得如果要选定某一个字符的话,要用【/字符/】这样选定。

sed d参数删除指定字符所在行的命令演示

c:替换,c的后面可以接字符,可以替换n1到n2之间的行的内容

例如:nl /etc/passwd |sed '5,10c I love asoul'

命令解释:将/etc/passwd文件的5到10行的内容替换成“I love asoul”

sed function c参数演示

s:替换,通常s可以搭配正则表达式进行使用来替换想要的内容,和之前我们在vim编辑器中学到的替换命令类似,可以使用【's/要被替换的字符/新字符/g'】这个格式来进行使用,比如:sed '1,20s/old/new/g'

例如:我们使用ifconfig eth0命令可以查看当前计算机的各种网络参数

ifconfig eth0命令演示

现在我想单独提取出'inet'这一行的数据,使用管道命令grep 'inet '进行筛选

ifconfig + grep筛选字符命令演示

好,现在我想单独将本机的IP地址取出来,我应该如何操作?

这时候就需要使用sed的替换功能了,核心的思路就是将除了IP地址以外的数据进行一个空白替换【s/想替换的字符//g】

命令:ifconfig eth0 |grep 'inet ' |sed 's/^.*inet'//g |sed 's/ netmask.\*$'//g'

(2) printf命令:数据格式化输出

printf命令常常用于我们数据输出的格式化排版,例如我们可以输出一个具有统一格式的表格。

命令格式:printf '打印格式' 实际内容

注意:printf并不是管道命令,日常如果要输出一个文件的内容给它进行处理的话,需要配合【$(command)】使用

① printf常用的变量格式

%ns:n代表数字,s代表字符串string,这个变量代表的意思是,你想要输出字符串的内容能够占多少个字符(固定字符串长度)

%ni:n代表数字,i代表整数integer,这个变量代表的意思是,你想要输出的整数的内容能够占多少个字符(固定整数的长度)

%N.nf:N和n都代表数字,f代表浮点数floating。如果有小数位数,例如我希望这个浮点数占10个字符,小数点占其中2位,则应该为%10.2f(固定浮点数的长度)

②printf常用的特殊样式格式

\a:警告声音输出

\t:输出水平【TAB】键

\v:输出垂直【TAB】键

\n:输出新的一行

\b:输出退格键(backspace)

\r:输出回车键

\f:清除屏幕(form feed)

\xNM:NM为两位数字,可以将数字转换为字符

print \xNM命令演示

③ 案例演示

例:我们查看printf.txt的这个内容

printf.txt文件内容

现在我想让Name这一列的字符串长度为6字段,整数为5个字段,浮点数为5个字段,但是只保留一位小数,且字段与字段之间用【TAB】键隔开,这时我该怎么输入命令?

命令:printf '%6s %5i %5i %5i %5.1f \n' $(cat printf.txt |grep -v Name)

记得配合$(command)使用,将文件内容输出给printf命令进行格式化处理

(3)awk命令:数据处理工具

awk工具也是一个数据处理工具,相较于sed的按一整行处理的特性,awk更擅长于将一行的内容用某一分隔符(默认是空格或【TAB】键)来分隔成多个字段进行处理。

例如【last】命令输出的内容,我们就可以根据【TAB】键来将其分隔成4个字段

last 输出内容

其中,awk分隔出来的字段,都是可以作为变量进行处理。第一个字段【root】就为$1,第二个字段【pts/1】是$2,以此类推。注意一个特殊变量$0,$0代表的意思是【一整列数据】,也就是所有(全部)域

命令格式:awk ‘条件类型1{操作1}条件类型2 {操作2}...’filename

awk命令经常和print命令配合使用,例如我想查看last输出的第一字段的内容的话,我输入:last |head -3 |awk '{print $1}'

awk的操作流程:

1.读入第一行,并将第1行的数据数据写入$0、$1、$2等变量中。

2.根据“条件类型”的限制,判断是否需要进行后面的操作

3.完成所以操作与条件类型

4.若还有后续的【行】的数据,则重复1~3的步骤,直到所有的数据都读完为止

注意:awk后续的所有条件和操作,都是靠单引号【‘’】括起来,然后每个操作都由单独的大括号【{}】括起来,其中的非变量内容(例如文本和特殊格式符号【\t \n等】)由单独的双引号【“”】括起来。

① awk的内置变量

awk以行为每一次的处理单位,以字段为最小的处理单位。那awk到底是怎么判断当前要处理的这个数据有几行和几列呢?

这时候就要使用到awk的几个内置变量了

NF:每一行($0)的字段总数

NR:目前awk所处理的是第几行数据

FS:目前的分隔字符,默认是空格键

例1:我想列出last前5行的每一行帐号($1)和当前处理的行数(NR)并且说明当前的字段总数,那我就应该输入:

last |head -5 |awk '{print $1 "\t lines:" NR "\t columns:" NF}'

awk NR NF内置变量演示

② awk的逻辑运算符

这个逻辑运算符在awk要进行条件判断时进行使用

>:大于

<:小于

>=:大于或等于

<=:小于或等于

==:等于

!=:不等于

例1:现在我们想查看/etc/passwd文件第三字段UID小于5的帐号列表,且输出结果仅列出UID和帐号,我们的命令应该是?

由于/etc/passwd文件的内容,是使用冒号【:】进行分隔的,所以我们的命令中,需要更改awk的默认分隔符

命令:cat /etc/passwd |awk '{FS=":"} $3<5 {print $1 "\t" $3}'

awk 命令+逻辑判断演示1

但是这时候我们看输出结果,我们会发现第一行的内容没有按照我们的想法来进行输出,这是因为默认awk在重新设定"FS"的内置变量后,是在第二行才开始进行生效的,如果我们想从第一行就开始生效,那我们就需要使用关键词【BEGIN】预设awk的变量。

新命令:cat /etc/passwd |awk 'BEGIN {FS=":"} $3<5 {print $1 "\t" $3}'

awk 命令+逻辑判断演示 2

(4)diff命令:文件比对工具

diff常常用于同一个软件包不同版本之间,以及原始文件和配置文件之间内容的比对,或者是比对两个目录之间的差异

命令格式:diff 【-b B i】 原始文件 目标文件

注意:原始文件或目标文件可以用【-】替换,代表【标准输入】

常用参数:

-b:忽略行之中的空白格,例如【about me】和【about me】看成一样的东西

-B:忽略空白行

-i:忽略大小写差异

例如:diff -i old.file new.file

diff /etc/rc0.d /etc/rc5.d

diff命令比对目录演示

(5) pr命令:文件打印设置

pr命令直接使用能够让我们打印linux系统内的文本文件,并附带标题和页码

pr命令演示