Linux正则与文本处理工具(10)

2023-02-21,,

该系列文章只是本人的学习笔记,文章中的文字描述提取自《Linux鸟哥私房菜》《Linux运维之道》等书中的重点内容,化繁为简能够在工作中快速复习掌握重点,并不代表个人立场,但转载请加出处,并注明参考文献。

正则表达式 (Regular Expression, RE, 或称为常规表达式)是通过一些特殊字符的排列,用于『查找/替换/删除』一行或多行文字或字符串,简单的说,正则表达式就是用在字串的处理上面的一种『表示公式』,正则表达式并不是一个工具程序,而是一个对字符串处理的标准依据,如果您想要以正则表达式的方式处理字串,就得要使用支持正则表达式的工具程序才行,这类的工具程序很多,例如 vi,vim,sed,awk,gawk,egrep等.

正则表达式,对于系统管理员来说是非常重要的,因为系统会产生很多的信息,这些信息有的重要有的仅是警告,此时管理员可以通过正则表达式来过滤出相应的我们需要的字段,你最好掌握这门技术,会对将来的数据分析,主机管理起到很大的帮助.

基础正则表达式

在上一章说过正则表达式和通配符的区别,(正则表达式用来在文件中匹配符合条件的字符串,而通配符则是用来匹配符合条件的文件名)吗? 其实这种区别只在Shell当中适用,因为用来在文件当中搜索字符串的命令,如 grep、awk、sed 等命令可以支持正则表达式,而在系统当中搜索文件的命令,如 ls、find、cp 这些命令不支持正则表达式,所以只能使用shell自己的通配符来进行匹配了.

首先我们先来学习一下基础的正则表达式吧,下面是我们常用的正则语法,我会给每一个语法,举一个小例子,下面我们开始学习吧.

实例1: 使用 * 实现匹配前一个字符出现0次或任意多次(本例中,则是匹配g字符出现0次或任意多次)

[root@localhost ~]# ls
wang wangg wanggg [root@localhost ~]# ls | grep "wang*"
wang
wangg
wanggg
[root@localhost ~]# ls | grep "wan*g*"
wang
wangg
wanggg

实例2: 使用 . 实现匹配除换行符以外的任意一个字符,(只匹配一个字符),一般我们把它叫做贪婪匹配

[root@localhost ~]# ls
wang wangg wanggg wangr [root@localhost ~]# ls | grep "wang"
wang
[root@localhost ~]# ls | grep "wang."
wangg
wangr
[root@localhost ~]# ls | grep "wang.."
wanggg

实例3: 使用 ^ 实现匹配行首是指定字符的行

[root@localhost ~]# ls
alert lyshark tcpl wakaka wang wangg wanggg woxin [root@localhost ~]# ls | grep "^ly"
lyshark
[root@localhost ~]# ls | grep "^wa"
wakaka
wang
wangg
wanggg
[root@localhost ~]# ls | grep "^a"
alert

实例4: 使用 $ 实现匹配行尾是指定字符的行

[root@localhost ~]# ls
alert lyshark tcpl wakaka wang wangg wanggg woxin [root@localhost ~]# ls | grep "a$"
wakaka
[root@localhost ~]# ls | grep "ark$"
lyshark
[root@localhost ~]# ls | grep "^w" | grep "n$" #匹配开头是w结尾是n的
woxin

实例5: 使用 [] 实现匹配中括号内任意一个字符,(只匹配其中一个)

[root@localhost ~]# ls
ale1t ale2t ale3t aleat alebt alert [root@localhost ~]# ls | grep "ale[a-z]t"
aleat
alebt
alert
[root@localhost ~]# ls | grep "ale[0-9]t"
ale1t
ale2t
ale3t
[root@localhost ~]# ls | grep "ale[ab]t"
aleat
alebt

实例6: 使用 [^] 实现匹配除了中括号字符以外的任意一个字符(^取反的意思)

[root@localhost ~]# ls
ale1t ale2t ale3t aleat aleAt aleBB alebt aleCT aleEt alert [root@localhost ~]# ls | grep "ale[^0-9]t"
aleat
aleAt
alebt
aleEt
alert
[root@localhost ~]# ls | grep "ale[^A-Z]t"
ale1t
ale2t
ale3t
aleat
alebt
alert
[root@localhost ~]# ls | grep "ale[^AE]t"
ale1t
ale2t
ale3t
aleat
alebt
alert

实例7: 使用 ^[^] 实现匹配行首是与不是指定字符的行

[root@localhost ~]# ls
ale1t ale2t lyshark tcpl wakaka wang wangg wanggg woxin [root@localhost ~]# ls | grep ^[a] #匹配行首是a开头的
ale1t
ale2t
[root@localhost ~]# ls | grep ^[^a] #匹配行首不是a开头的
lyshark
tcpl
wakaka
wang
wangg
wanggg
woxin

实例8: 使用 \{n}\ 实现匹配前面的字符恰好出现了n次的行

[root@localhost ~]# ls
12333 13466614578 13562653874 172.168.1.2 18264758942 192.168.1.1 45666 78999 [root@localhost ~]# ls | grep "123\{3\}"
12333
[root@localhost ~]# ls | grep "[0-9][0-9][0-9]\{3\}" #匹配前两个字符是0-9的,最后一个字符出现过3次的
12333
45666
78999
[root@localhost ~]# ls | grep "[1][3-8][0-9]\{9\}" #匹配手机号规则
13466614578
13562653874
18264758942

实例9: \{n,}\ 实现匹配前面的字符的出现,不小于n次的行

[root@localhost ~]# ls
12333 123333 1233333 [root@localhost ~]# ls | grep "123\{3,\}" #前一个字符3的出现不小于3次
12333
123333
1233333
[root@localhost ~]# ls | grep "123\{4,\}" #前一个字符3的出现不小于4次
123333
1233333

实例10: \{n,m}\ 实现匹配前面的字符出现,不小于n次,最多出现m次的行

[root@localhost ~]# ls
123 1233 12333 123333 1233333 12333333 123333333 1233333333 12333333333 [root@localhost ~]# ls |grep "123\{3,5\}" #前一个字符3最少出现3次,最多不大于5次
12333
123333
1233333

总结:匹配一个合法IP地址

[root@localhost ~]# ls
10.10.10.22 127.9.0.8 172.168.1.2 192.168.1.1 192.168.1.3 255.255.255.255
127.0.0.1 172.168.1.1 172.168.1.3 192.168.1.2 192.199.256.256 256.256.256.256 [root@localhost ~]# ls | egrep "^(([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|
2[0-4][0-9]|25[0-4])$" 10.10.10.22
172.168.1.1
172.168.1.2
172.168.1.3
192.168.1.1
192.168.1.2
192.168.1.3

总结:匹配一个合法邮箱地址

[root@localhost ~]# ls
1181506874@qq.com lysharks@163.cn lysharks@163.com wangrui@126.com wangrui@129.cc [root@localhost ~]# ls | egrep "^[0-9a-zA-Z][0-9a-zA-Z_]{1,16}[0-9a-zA-Z]\@[0-9a-zA-Z-]*([0-9a-zA-Z])?\.(com|com.cn|net|
org|cn)$" 1181506874@qq.com
lysharks@163.cn
lysharks@163.com
wangrui@126.com

扩展正则表达式

事实上,一般用户只要了解基础型的正则语句就已经相当足够了,不过,在某些时候,为了简化命令的长度,还是需要扩展正则的支持的,打个比方,我们可以使用 cat xxx.log | grep -v '^$' |grep -v '^#' 来实现过滤文本中的注释行,和空白行,但是这不够精简,它的执行还是会用到两次的过滤,如果使用扩展正则表达式,我们可以这样写,cat xxx.log |egrep -v '^$|^#' 这样一个命令就可实现上面的效果啦,此处我们需要说明的是,grep -E 和egrep效果是相同的,使用哪一个都一个样.

熟悉了基础正规表达式之后,再来看这个扩展正则表达式,是不是很轻松啊,亲,下面我们就来分别说明这几个符号的使用规则吧.

实例1: + 实现匹配前一个字符出现1次或任意多次

[root@localhost ~]# ls
gogle google gooogle gooogooogle goooogle gooooogle goooooogle [root@localhost ~]# ls | grep -E "go+gle"
gogle
google
gooogle
goooogle
gooooogle
goooooogle

实例2: ? 实现匹配前一个字符出现0次,或1次

[root@localhost ~]# ls
gogle google gooogle gooogooogle goooogle gooooogle goooooogle [root@localhost ~]# ls | grep -E "go?gle"
gogle
[root@localhost ~]# ls | grep -E "goo?gle"
gogle
google

实例3: | 实现匹配两个或多个分支选择

[root@localhost ~]# ls
alert lyshark rui wang [root@localhost ~]# ls | grep -E "alert|lyshark"
alert
lyshark
[root@localhost ~]# ls | grep -E "wang|rui|alert"
alert
rui
wang

实例4: () 实现将字符作为一个整体匹配,即模式单元

[root@localhost ~]# ls
dog dogdog dogdogdog hello_lyshark hello_world [root@localhost ~]# ls | grep -E "(dog)+"
dog
dogdog
dogdogdog
[root@localhost ~]# ls | grep -E "hello_(world|lyshark)"
hello_lyshark
hello_world

Grep 行处理工具

grep (global search regular expression(RE) and print out the line 全面搜索正则表达式并把行打印出来)是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹配的行打印出来,好了废话不多说,先看命令格式.

[root@localhost ~]# grep --help

命令语法:[ grep [选项] [过滤菜单] 文件名 ]

        -a              #将二进制数据一同列出
-c #计算找到查找字符串的次数
-i #忽略大小写差异
-n #顺便标号显示
-v #反选参数
-q #不显示任何提示信息,安静模式
-E #使用扩展正则,egrep
-A #匹配指定字符的后n个字符
-B #匹配指定字符的前n个字符
-C #匹配指定字符的前n个和后n个字符

实例1: 使用 grep -n 参数过滤数据时,一同标号

[root@localhost ~]# cat /etc/passwd | grep -n "root"

1:root:x:0:0:root:/root:/bin/bash
10:operator:x:11:0:operator:/root:/sbin/nologin

实例2: 使用 grep -v 反选打印,行中不包括/bin/bash的行

[root@localhost ~]# cat /etc/passwd | grep -vn "/bin/bash"

2:bin:x:1:1:bin:/bin:/sbin/nologin
3:daemon:x:2:2:daemon:/sbin:/sbin/nologin
4:adm:x:3:4:adm:/var/adm:/sbin/nologin
5:lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
6:sync:x:5:0:sync:/sbin:/bin/sync
7:shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
8:halt:x:7:0:halt:/sbin:/sbin/halt
....省略....

实例3: 使用 grep -i 过滤出不论大小写的lyshark单词的行

[root@localhost ~]# cat lyshark.log
LyShark
lyshark
admin
Admin
ADMIN
Good
GOOD [root@localhost ~]# cat lyshark.log | grep -ni "lyshark"
1:LyShark
2:lyshark

实例4: 使用正则,过滤出开头是 ^L 的行的内容

[root@localhost ~]# cat lyshark.log
LyShark
lyshark
admin
Admin
ADMIN
Good
GOOD [root@localhost ~]# cat lyshark.log | grep -n "^L"
1:LyShark

实例5: 使用正则,过滤出开头不是 ^L的行的内容

[root@localhost ~]# cat lyshark.log
LyShark
lyshark
admin
Admin
ADMIN
Good
GOOD [root@localhost ~]# cat lyshark.log | grep -n "[^L]yshark"
2:lyshark

实例6: 使用正则,过滤出开头是小写字母的行

[root@localhost ~]# cat lyshark.log
LyShark
lyshark
admin
Admin
ADMIN
Good
GOOD
123123
1233
66431
124adb [root@localhost ~]# cat lyshark.log | grep -n "^[a-z]"
2:lyshark
3:admin

实例7: 使用正则,过滤出开头不是,a-z或A-Z的行

[root@localhost ~]# cat lyshark.log
LyShark
lyshark
admin
Admin
ADMIN
Good
GOOD
123123
1233
66431
124adb [root@localhost ~]# cat lyshark.log | grep -n "^[^a-zA-Z]"
8:123123
9:1233
10:66431
11:124adb

实例8: 使用正则,找出结尾是小数点的哪一行

[root@localhost ~]# cat lyshark.log
LyShark
lyshark
admin
Admin
ADMIN
Good
123123
1233.
66431. [root@localhost ~]# cat lyshark.log |grep -n "\.$"
8:1233.
9:66431.

实例9: 使用正则,过滤掉开头是#号的,和开头是空行的行

[root@localhost ~]# cat lyshark.log
#LyShark
#lyshark
#admin
#Admin ADMIN
Good 123123 1233.
66431. [root@localhost ~]# cat lyshark.log | grep -v "^#" | grep -v "^$"
ADMIN
Good
123123
1233.
66431.

实例10: 使用正则,过滤出前一个字符o刚好出现两次的行

[root@localhost ~]# cat lyshark.log
#LyShark
#lyshark
#admin
#Admin ADMIN
Good
123123
1233.
66431. [root@localhost ~]# cat lyshark.log |grep -n "o\{2\}"
7:Good

实例11: 使用正则,匹配开头是0-9且结尾是点的行

[root@localhost ~]# cat lyshark.log
#LyShark
#lyshark
#admin
#Admin ADMIN
Good
123123
1233.
66431. [root@localhost ~]# cat lyshark.log |grep -n -E "^[0-9]+\."
9:1233.
10:66431.

实例12: 使用正则,匹配指定字符的后2个字符,或前2个字符

[root@localhost ~]# cat lyshark.log
lyshark
LySHARK
wang
rui
hello
world
alert
123123
45678 [root@localhost ~]# cat lyshark.log |grep -n -A 2 "hello"
5:hello
6-world
7-alert [root@localhost ~]# cat lyshark.log |grep -n -B 2 "hello"
3-wang
4-rui
5:hello

Cut 列提取工具

cut命令用来显示行中的指定部分,删除文件中指定字段,cut经常用来显示文件的内容,类似于type命令.该命令有两项功能,其一是用来显示文件的内容,它依次读取由参数file所指明的文件,将它们的内容输出到标准输出上.其二是连接两个或多个文件,如cut fl f2 > f3 将把文件fl和f2的内容合并,然后通过输出重定向符">" 的作用,将它们放入文件f3中.

首先我们来看一下它的格式吧,如下:

[root@localhost ~]# cut --help

命令语法:[ cut [选项] [列号] 文件名 ]

        -f              #-f 列号:指定提取第几列
-d #-d 分隔符:按照指定分隔符进行分割
-c #-c 字符范围:不依赖分割符来分割,而是通过字符范围进行字段提取
-m #表示从第一个字符提取到第m个
-b #仅显示行中指定直接范围的内容
-n #与"-b"选项连用,不分割多字节字符
n- #表示从第n个字符开始提取到结尾
n-m #表示从第n提取到第m个字符 --complement #补齐被选择的字节,字符或字段
--out-delimiter=<字段分隔符> #指定输出内容是的字段分割符

手动创建一个文本,添加内容列之间用tab分隔,用来测试后续内容

[root@localhost ~]# cat lyshark.log

ID      NAME    AGE     Gender  Mark
1 WR 22 m 100
2 LC 26 m 90
3 LY 23 m 88

实例1: 通过使用 -f 选项指定过滤的列,并显示到屏幕

[root@localhost ~]# cat lyshark.log

ID      NAME    AGE     Gender  Mark
1 WR 22 m 100
2 LC 26 m 90
3 LY 23 m 88 [root@localhost ~]# cut -f 2 lyshark.log
NAME
WR
LC
LY [root@localhost ~]# cut -f 2,5 lyshark.log
NAME Mark
WR 100
LC 90
LY 88

实例2: 通过使用 --complement 选项提取指定字段之外的列,(打印除了第2列之外的列)

[root@localhost ~]# cat lyshark.log

ID      NAME    AGE     Gender  Mark
1 WR 22 m 100
2 LC 26 m 90
3 LY 23 m 88 [root@localhost ~]# cut -f 2 --complement lyshark.log ID AGE Gender Mark
1 22 m 100
2 26 m 90
3 23 m 88

实例3: 通过使用 -c 选项过滤/etc/passwd,并打印第1个到第3个字符

[root@localhost ~]# cut -c 1-3 /etc/passwd
roo
bin
dae
adm
lp:
syn
....省略....

实例4: 通过使用 -c -2 选项过滤/etc/passwd,并打印前2个字符

[root@localhost ~]# cut -c -2 /etc/passwd
ro
bi
da
ad
lp
....省略....

实例5: 通过使用 -c 5- 选项过滤/etc/passwd,打印从第5个字符开始到结尾

[root@localhost ~]# cut -c 5- /etc/passwd
:x:0:0:root:/root:/bin/bash
x:1:1:bin:/bin:/sbin/nologin
on:x:2:2:daemon:/sbin:/sbin/nologin
x:3:4:adm:/var/adm:/sbin/nologin
....省略....

实例6: 通过使用 -d 指定分隔符 -f 指定打印第个字段,以下我们分别截取第1和第7个字段

[root@localhost ~]# cut -d ":" -f 1,7 /etc/passwd
root:/bin/bash
bin:/sbin/nologin
daemon:/sbin/nologin
adm:/sbin/nologin
lp:/sbin/nologin
sync:/bin/sync
....省略....

实例7: 通过使用 -c -3 指定截取前3个字符,还可以通过 -c 3 截取第3个字符

[root@localhost ~]# cut -c -3 /etc/passwd
roo
bin
dae
adm
lp:
....省略....
[root@localhost ~]# cut -c 3 /etc/passwd
o
n
e
m
:
n
....省略....

Sed 流编辑器

sed是一种流编辑器,它是文本处理中非常中的工具,能够完美的配合正则表达式使用,功能不同凡响.处理时,把当前处理的行存储在临时缓冲区中,称为 "模式空间" (pattern space),接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕,接着处理下一行,这样不断重复,直到文件末尾.文件内容并没有改变,除非你使用重定向存储输出.Sed主要用来自动编辑一个或多个文件,简化对文件的反复操作,编写转换程序等.

sed主要是来进行数据选取,替换,删除,新增的命令,二话不说先看一下它的参数吧.

[root@localhost ~]# sed --help

命令语法:[ sed [选项] [范围] [动作] 文件名 ]

        -n              #把经过sed命令处理的行输出到屏幕
-e #允许对输入数据应用多条sed命令编辑
-f #从sed脚本中读入sed操作,和awk命令的-f类似
-r #在sed中支持扩展正则表达式
-i #用sed的修改结果,写到文件 命令动作: p #打印,输出指定的行
a #追加,在当前行后添加一行或多行
i #插入,在当前行前插入一行或多行
c #整行替换,用c后面的字符串替换原数据行
d #删除,删除指定的行
s #字串替换,格式:"行范围s/旧字串/新字串/g" #对sed命令我们要知道的是,它所有的修改都不会直接修改文件的内容,而是在内存中进行处理然后打印到屏幕上
#如果想要写入文件,请使用 sed -i 选项才会保存到文本中.

在进行实验之前,首先创建一个文件,来做测试用

[root@localhost ~]# cat lyshark.log

ID      NAME    AGE     Gender  Mark
1 WR 22 m 100
2 LC 26 m 90
3 LY 23 m 88
4 XDL 40 b 100

实例1: 使用 sed '2p' 重复打印第二行数据

[root@localhost ~]# cat lyshark.log
ID NAME AGE Gender Mark
1 WR 22 m 100
2 LC 26 m 90
3 LY 23 m 88
4 XDL 40 b 100 [root@localhost ~]# sed '2p' lyshark.log
ID NAME AGE Gender Mark
1 WR 22 m 100
1 WR 22 m 100 ←本行是2p打印的
2 LC 26 m 90
3 LY 23 m 88
4 XDL 40 b 100 [root@localhost ~]# sed '3p' lyshark.log
ID NAME AGE Gender Mark
1 WR 22 m 100
2 LC 26 m 90
2 LC 26 m 90 ←本行是2p打印的
3 LY 23 m 88
4 XDL 40 b 100

实例2: 使用 sed -n 限定,只选取指定的行进行显示

[root@localhost ~]# cat lyshark.log
ID NAME AGE Gender Mark
1 WR 22 m 100
2 LC 26 m 90
3 LY 23 m 88
4 XDL 40 b 100 [root@localhost ~]# sed -n '2p' lyshark.log ←只打印第2行数据
1 WR 22 m 100 [root@localhost ~]# sed -n '1p' lyshark.log ←只打印第1行数据
ID NAME AGE Gender Mark

实例3: 使用 sed '2,4d' 删除掉文件2-4行,并显示到屏幕,(原文件内容并没有被修改)

[root@localhost ~]# cat lyshark.log
ID NAME AGE Gender Mark
1 WR 22 m 100
2 LC 26 m 90
3 LY 23 m 88
4 XDL 40 b 100 [root@localhost ~]# sed '2,4d' lyshark.log ←删除2-4行的数据并打印
ID NAME AGE Gender Mark
4 XDL 40 b 100 [root@localhost ~]# sed '1d' lyshark.log ←删除第1行的数据
1 WR 22 m 100
2 LC 26 m 90
3 LY 23 m 88
4 XDL 40 b 100

实例4: 使用 sed '2[a|i]' 追加,或者插入数据指定数据

[root@localhost ~]# cat lyshark.log
ID NAME AGE Gender Mark
1 WR 22 m 100
2 LC 26 m 90
3 LY 23 m 88
4 XDL 40 b 100 [root@localhost ~]# sed '2a hello lyshark' lyshark.log ←在第2行后面追加数据
ID NAME AGE Gender Mark
1 WR 22 m 100
hello lyshark
2 LC 26 m 90
3 LY 23 m 88
4 XDL 40 b 100 [root@localhost ~]# sed '2i hello lyshark' lyshark.log ←在第2行前面插入数据
ID NAME AGE Gender Mark
hello lyshark
1 WR 22 m 100
2 LC 26 m 90
3 LY 23 m 88
4 XDL 40 b 100

实例5: 使用 \ 换行符,一次插入多行数据

[root@localhost ~]# cat lyshark.log
ID NAME AGE Gender Mark
1 WR 22 m 100
2 LC 26 m 90
3 LY 23 m 88
4 XDL 40 b 100 [root@localhost ~]# sed '2a hello \ ←在第二行下面,插入一段话,用\隔开
> my name is lyshark \
> age 22 \
> boy ' lyshark.log ID NAME AGE Gender Mark
1 WR 22 m 100
hello
my name is lyshark
age 22
boy
2 LC 26 m 90
3 LY 23 m 88
4 XDL 40 b 100

实例6: 使用 sed 'c' 实现整行替换数据

[root@localhost ~]# cat lyshark.log
ID NAME AGE Gender Mark
1 WR 22 m 100
2 LC 26 m 90
3 LY 23 m 88
4 XDL 40 b 100 [root@localhost ~]# cat lyshark.log | sed '5c 5 WRS 99 m 111' ←整行替换第5行内容
ID NAME AGE Gender Mark
1 WR 22 m 100
2 LC 26 m 90
3 LY 23 m 88
5 WRS 99 m 111

实例7: 使用 sed 后面跟上 -i 选项,将第5行的修改,保存进文件,(-i选项是回写)

[root@localhost ~]# cat lyshark.log
ID NAME AGE Gender Mark
1 WR 22 m 100
2 LC 26 m 90
3 LY 23 m 88
4 XDL 40 b 100 [root@localhost ~]# sed -i '5c 5 WRS 99 m 111' lyshark.log [root@localhost ~]# cat lyshark.log
ID NAME AGE Gender Mark
1 WR 22 m 100
2 LC 26 m 90
3 LY 23 m 88
5 WRS 99 m 111 ←这里的数据已经写入成功

实例8: 字符串的替换 sed 's/旧文本/新文本/g' 进行整行替换

[root@localhost ~]# cat lyshark.log
ID NAME AGE Gender Mark
1 WR 22 m 100
2 LC 26 m 90
3 LY 23 m 88
5 WRS 99 m 111 [root@localhost ~]# sed '2s/WR/LyShark/g' lyshark.log ←将第2行的,WR替换成LyShark
ID NAME AGE Gender Mark
1 LyShark 22 m 100
2 LC 26 m 90
3 LY 23 m 88
5 WRS 99 m 111 [root@localhost ~]# sed '3s/LC/Admin/g' lyshark.log ←将第3行的,LC替换成Admin
ID NAME AGE Gender Mark
1 WR 22 m 100
2 Admin 26 m 90
3 LY 23 m 88
5 WRS 99 m 111 #注意:上方只是替换打印,并没有保存,如若想保存请加 -i 属性

实例9: 将第3行数据的开头添加#注释

[root@localhost ~]# cat lyshark.log
ID NAME AGE Gender Mark
1 WR 22 m 100
2 LC 26 m 90
3 LY 23 m 88
5 WRS 99 m 111 [root@localhost ~]# sed '3s/^/#/g' lyshark.log
ID NAME AGE Gender Mark
1 WR 22 m 100
#2 LC 26 m 90
3 LY 23 m 88
5 WRS 99 m 111

实例10: 将第4和第5行的内容替换成空

[root@localhost ~]# cat lyshark.log
ID NAME AGE Gender Mark
1 WR 22 m 100
2 LC 26 m 90
3 LY 23 m 88
5 WRS 99 m 111 [root@localhost ~]# sed -e '4s/LY//g ; 5s/WRS//g' lyshark.log
ID NAME AGE Gender Mark
1 WR 22 m 100
2 LC 26 m 90
3 23 m 88
5 99 m 111 #sed 要进行多行操作时,只能通过 -e 写多条操作语句,用 ; 或回车分隔

过滤IP地址小实验

[root@localhost ~]# ifconfig
ens32: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.1.10 netmask 255.255.255.0 broadcast 192.168.1.255
inet6 fe80::897c:d72d:cd95:b9ec prefixlen 64 scopeid 0x20<link>
ether 00:0c:29:b1:b7:be txqueuelen 1000 (Ethernet)
RX packets 2344 bytes 156370 (152.7 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 454 bytes 50049 (48.8 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 [root@localhost ~]# ifconfig | grep "inet 192" |sed 's/^.*inet //g'
192.168.1.10 netmask 255.255.255.0 broadcast 192.168.1.255 [root@localhost ~]# ifconfig |grep "inet 192" |sed 's/^.*inet //g' |sed 's/ netmask.*$//g'
192.168.1.10

Printf 文本格式化

[root@localhost ~]# printf --help

输出类型:
%ns: #输出字符串,n是数字,指代输出几个字符
%ni: #输出证书,n是数字,指代输出几个数字
%m.nf: #输出浮点数,m和n是数字,指代输出整数位和小数位 输出格式:
\a: #输出警告音
\b: #输出退格键,也就是Backspace键
\f: #清屏
\n: #换行符
\r: #回车,也就是Enter键
\t: #水平输出退格键,也就是Tab键
\v: #垂直输出退格键,也就是Tab键 #注意:print 和 printf 的主要区别在有 printf 是标准的格式化输出,必须手动指定换行和tab.

在进行实验之前,首先创建一个文件,来做测试用

[root@localhost ~]# cat lyshark.log

ID      NAME    AGE     Gender  Mark
1 WR 22 m 100
2 LC 26 m 90
3 LY 23 m 88
4 XDL 40 b 100

实例1: 通过 printf 函数打印文本内容

printf命令,如果不指定输出格式,则会把所有输出内容连在一起输出,其实文本的输出本身就是这样的,cat等文本输出命令之所以可以按照格式漂亮的输出,那是因为cat命令已经设定了输出格式.

[root@localhost ~]# printf '%s' $(cat lyshark.log)

IDNAMEAGEGenderMark1WR22m1002LC26m903LY23m884XDL40b100

实例2: 通过 printf 格式化后输出一段文本

[root@localhost ~]# printf '%s\t %s\t %s\t %s\t %s\t \n' $(cat lyshark.log)

ID       NAME    AGE     Gender  Mark
1 WR 22 m 100
2 LC 26 m 90
3 LY 23 m 88
4 XDL 40 b 100

实例3: 通过 printf 按照整数型和浮点型输出,则需要修改

[root@localhost ~]# cat lyshark.log
ID NAME AGE Gender Mark
1 WR 22 m 100
2 LC 26 m 90
3 LY 23 m 88
4 XDL 40 b 100 [root@localhost ~]# printf '%i\t %s\t %8.2f\t %s\t %s\t \n' $(cat lyshark.log |grep -v ID)
1 WR 22.00 m 100
2 LC 26.00 m 90
3 LY 23.00 m 88
4 XDL 40.00 b 100

## Awk 正则表达工具

awk是一种编程语言,用于在linux/unix下对文本和数据进行处理,数据可以来自标准输入(stdin),一个或多个文件,或其它命令的输出.它支持用户自定义函数和动态正则表达式等先进功能,是linux/unix下的一个强大编程工具,它在命令行中使用,但更多是作为脚本来使用.awk有很多内建的功能,比如数组、函数等,这是它和C语言的相同之处,灵活性是awk最大的优势.

在开始看例子之前,老样子,你懂的

[root@localhost ~]# awk --help
Usage: awk [POSIX or GNU style options] -f progfile [--] file ...
Usage: awk [POSIX or GNU style options] [--] 'program' file ... 命令语法:[ awk '条件1{动作1} 条件2{动作2} ....' 文件名 ] 条件:一般使用关系表达式作为条件
x>10 判断x变量是否大于10
x==y 判断变量x是否等于变量y
A ~ B 判断字符串A中是否包含能匹配B表达式的字符串
A!~ B 判断字符串A中是否不包含能匹配B表达式的字符串

在进行实验之前,首先创建一个文件,来做测试用

[root@localhost ~]# cat lyshark.log

ID      NAME    AGE     Gender  Mark
1 WR 22 m 100
2 LC 26 m 90
3 LY 23 m 88
4 XDL 40 b 100

◆awk常用例子◆

实例1: 使用awk格式化输出第二列和第三列的内容

[root@localhost ~]# cat lyshark.log
ID NAME AGE Gender Mark
1 WR 22 m 100
2 LC 26 m 90
3 LY 23 m 88
4 XDL 40 b 100 [root@localhost ~]# awk '{ printf $2 "\t" $3 "\n" }' lyshark.log
NAME AGE
WR 22
LC 26
LY 23
XDL 40

实例2: 通过awk命令截取Size和Used命令的显示列

[root@localhost ~]# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/centos-root 8.0G 1.2G 6.9G 15% /
devtmpfs 98M 0 98M 0% /dev
tmpfs 110M 0 110M 0% /dev/shm
tmpfs 110M 5.5M 104M 5% /run
tmpfs 110M 0 110M 0% /sys/fs/cgroup
/dev/sda1 1014M 130M 885M 13% /boot
tmpfs 22M 0 22M 0% /run/user/0 [root@localhost ~]# df -h |awk '{ printf $2 "\t" $3 "\n" }'
Size Used
8.0G 1.2G
98M 0
110M 0
110M 5.5M
110M 0
1014M 130M
22M 0

◆begin 与 end◆

BEGIN是awk的保留字,是一种特殊的条件类型.BEGIN的执行时机是 "在awk程序一开始时,尚未读取任何数据之前执行",一旦BEGIN后的动作执行一次,当awk开始从文件中读入数据,BEGIN的条件就不再成立,所以BEGIN定义的动作只能被执行一次,例如:

下面的,整个动作定义了两个动作,先打印 "执行语句前,执行我" 然后输出过滤后的2和4列

[root@localhost ~]# cat lyshark.log
ID NAME AGE Gender Mark
1 WR 22 m 100
2 LC 26 m 90
3 LY 23 m 88
4 XDL 40 b 100 [root@localhost ~]# awk 'BEGIN{ printf "执行语句前,执行我 \n" }{ printf $2 "\t" $4 "\n" }' lyshark.log
执行语句前,执行我
NAME Gender
WR m
LC m
LY m
XDL b

END也是awk保留字,不过刚好和BEGIN相反,END是在awk程序处理完所有数据,即将结束时执行.END后的动作只在程序结束时执行一次.

[root@localhost ~]# cat lyshark.log
ID NAME AGE Gender Mark
1 WR 22 m 100
2 LC 26 m 90
3 LY 23 m 88
4 XDL 40 b 100 [root@localhost ~]# awk 'END{ printf "执行语句结束后,执行我 \n" }{ printf $2 "\t" $4 "\n" }' lyshark.log
NAME Gender
WR m
LC m
LY m
XDL b
执行语句结束后,执行我

BEGIN与END连用,以下例子也就是实现了,开始前执行打印 "执行语句前,先执行我",结束时执行打印 "执行语句后,在执行我"

[root@localhost ~]# cat lyshark.log
ID NAME AGE Gender Mark
1 WR 22 m 100
2 LC 26 m 90
3 LY 23 m 88
4 XDL 40 b 100 [root@localhost ~]# awk 'BEGIN{ printf "执行语句前,先执行我 \n" }END{ printf "执行语句后,在执行我 \n"}{ printf $2 "\t"
$4 "\n" }' lyshark.log 执行语句前,先执行我
NAME Gender
WR m
LC m
LY m
XDL b
执行语句后,在执行我

◆awk关系运算◆

关系运算符:设定条件,符合执行不符合不执行,下面设定条件为AGE字段>=25岁的列出

[root@localhost ~]# cat lyshark.log
ID NAME AGE Gender Mark
1 WR 22 m 100
2 LC 26 m 90
3 LY 23 m 88
4 XDL 40 b 100 [root@localhost ~]# cat lyshark.log | grep -v ID | awk '$3 >=25 {print $1 "\t" $2}'
2 LC
4 XDL

◆awk正则搜索◆

awk是列提取命令,第一步的动作却是先读入第一行,整个执行步骤:

⦁ 如果有BEGIN条件,则先执行BEGIN定义动作.

⦁ 如果没有BEGIN条件,则先读入第一行,把第一行的数据依次赋成$0 $1 $2 $3 …等变量,$0 代表整行数据,$1 则为第一个字段,依次类推.

⦁ 读入下一行,重复赋值变量,并打印数据.

指定搜索: 正则搜索第2列NAME字段,包含XDL的行,并打印出AGE列对应的值

[root@localhost ~]# cat lyshark.log
ID NAME AGE Gender Mark
1 WR 22 m 100
2 LC 26 m 90
3 LY 23 m 88
4 XDL 40 b 100 [root@localhost ~]# awk '$2 ~/XDL/ {printf $3 "\n"}' lyshark.log
40 [root@localhost ~]# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/centos-root 8.0G 1.2G 6.9G 15% /
devtmpfs 98M 0 98M 0% /dev
tmpfs 110M 0 110M 0% /dev/shm
tmpfs 110M 5.5M 104M 5% /run
tmpfs 110M 0 110M 0% /sys/fs/cgroup
/dev/sda1 1014M 130M 885M 13% /boot
tmpfs 22M 0 22M 0% /run/user/0 [root@localhost ~]# df -h | grep -v "Filesystem" | awk ' $1 ~/sda1/ {print $2}'
1014M

全局搜索: 正则全局搜索包含WR的字段行,并打印本行

[root@localhost ~]# cat lyshark.log
ID NAME AGE Gender Mark
1 WR 22 m 100
2 LC 26 m 90
3 LY 23 m 88
4 XDL 40 b 100 [root@localhost ~]# cat lyshark.log | awk '/WR/ {printf $0 "\n"}'
1 WR 22 m 100

屏蔽显示: 查看 df 的真实分区的使用情况,不看光盘和虚拟磁盘

[root@localhost ~]# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/centos-root 8.0G 1.2G 6.9G 15% /
devtmpfs 98M 0 98M 0% /dev
tmpfs 110M 0 110M 0% /dev/shm
tmpfs 110M 5.5M 104M 5% /run
tmpfs 110M 0 110M 0% /sys/fs/cgroup
/dev/sda1 1014M 130M 885M 13% /boot
tmpfs 22M 0 22M 0% /run/user/0 [root@localhost ~]# df -h | awk '/sda[0-9]/ {printf $1 "\t" $5"\n"}'
/dev/sda1 13%

◆awk内置变量◆

实例1: 通过内置变量 FS=":" 定义分隔符,打印/etc/passwd文件的第1列和第3列

[root@localhost ~]# cat /etc/passwd |grep "/bin/bash"
root:x:0:0:root:/root:/bin/bash [root@localhost ~]# cat /etc/passwd |grep "/bin/bash" | \
> awk 'BEGIN {FS=":"} {printf $1 "\t" $3 "\n"}' root 0

实例2: 打印行内容的同时,打印出行号(NR变量),和字段数(NF变量)

[root@localhost ~]# cat /etc/passwd | grep "/bin/bash" |
\ awk 'BEGIN{FS=":"} {print $1 "\t" $3 "\t" "行号: " NR "\t" "
字段数: " NF}' root 0 行号: 1 字段数: 7

实例3: 打印行内容,首先判断 $1==sshd 然后再打印本行的行号等信息

[root@localhost ~]# cat /etc/passwd | grep "/sbin/nologin" |
\ awk 'BEGIN{FS=":"}$1=="sshd" {print $1 "\t" $3 "\t" "行号
: " NR "\t" "字段数: " NF}' sshd 74 行号: 13 字段数: 7

实例4: 一个分区统计的小例子

[root@localhost ~]# df -h |grep -v "Filesystem" | \
awk '$1=="/dev/sda1" {print $1 "\t" $5 "\t" "行号:" NR "\t" "字段数:" NF }' /dev/sda1 13% 行号:6 字段数:6

◆awk流程控制◆

在awk编程中,因为命令语句非常长,输入格式时需要注意以下内容:

⦁ 多个条件{动作}可以用空格分割,也可以用回车分割.

⦁ 在一个动作中,如果需要执行多个命令,需要用";"分割,或用回车分割.

⦁ 在awk中,变量的赋值与调用都不需要加入"$"符.

⦁ 条件中判断两个值是否相同,请使用"==",以便和变量赋值进行区分.

实例1: 将第2行,到第4行,的内容加起来,最后输出结果

[root@localhost ~]# cat lyshark.log
ID NAME AGE SEX LINUX
1 LY 18 boy 100
2 SC 30 man 150
3 WR 22 man 90
4 ZSX 55 boy 96 [root@localhost ~]# awk 'NR==2{x=$3} NR==3{y=$3} NR==4{z=$3} {totle=x+y+z;print "Totle is:" totle}' lyshark.log
Totle is:0
Totle is:18
Totle is:48
Totle is:70
Totle is:70 [root@localhost ~]# awk 'NR==2{x=$3} NR==3{y=$3} NR==4{z=$3;totle=x+y+z;print "Totle is:" totle}' lyshark.log
Totle is:70

实例2: 统计AGE列,将年龄小于25岁的任过滤出来,并显示 is young man!

[root@localhost ~]# cat lyshark.log
ID NAME AGE SEX LINUX
1 LY 18 boy 10
2 SC 30 man 50
3 WR 22 man 90
4 ZSX 55 boy 96 [root@localhost ~]# cat lyshark.log | awk '{if (NR >= 2){if ($3 < 25) printf $2 " IS Young Man! \n"}}'
LY IS Young Man!
WR IS Young Man!

实例3: 统计LINUX列,当出现大于80分的,打印到屏幕上 is good man!

[root@localhost ~]# cat lyshark.log
ID NAME AGE SEX LINUX
1 LY 18 boy 10
2 SC 30 man 50
3 WR 22 man 90
4 ZSX 55 boy 96 [root@localhost ~]# cat lyshark.log | awk 'NR>=2 {temp=$5} temp>80 {printf $2 "IS Good Man!\n"}'
WRIS Good Man!
ZSXIS Good Man!

◆awk函数编写◆

实例1: 通过定义的函数格式,去匹配并传递参数

[root@localhost ~]# cat lyshark.log
ID NAME AGE SEX LINUX
1 LY 18 boy 10
2 SC 30 man 50
3 WR 22 man 90
4 ZSX 55 boy 96 [root@localhost ~]# cat lyshark.log | awk 'function lyshark(x,y) {printf x "\t" y "\n"}{lyshark($2,$5)}'
NAME LINUX
LY 10
SC 50
WR 90
ZSX 96

实例2: awk中调用脚本,对于小的单行程序来说,将脚本作为命令行自变量传递给awk是非常简单的,而对于多行程序就比较难处理.当程序是多行的时候,使用外部脚本是很适合的.首先在外部文件中写好脚本,然后可以使用awk的-f选项,使其读入脚本并且执.

[root@localhost ~]# cat passwd.awk
BEGIN {FS=":"}
{print $1 "\t" $3} [root@localhost ~]# cat /etc/passwd | awk -f passwd.awk
root 0
bin 1
daemon 2
adm 3
lp 4
sync 5
shutdown 6
halt 7
mail 8
operator 11
games 12
ftp 14
nobody 99
systemd-network 192
dbus 81
polkitd 999
sshd 74
postfix 89
chrony 998

## diff/patch 文件比对

什么时候会用到文件的比对啊? 通常是『同一个套装软件的不同版本之间,比较配置文件的差异』,很多时候所谓的文件比对,通常是用在 ASCII 纯文字档的比对上的,那么比对文件最常见的就是 diff .

diff命令在最简单的情况下,比较给定的两个文件的不同.如果使用 "-" 代替 "文件" 参数,则要比较的内容将来自标准输入,diff命令是以逐行的方式,比较文本文件的异同处,如果该命令指定进行目录的比较,则将会比较该目录中具有相同文件名的文件,而不会对其子目录文件进行任何比较操作.

◆diff 生成补丁文件◆

[root@localhost ~]# diff --help
Usage: diff [OPTION]... FILES
Compare FILES line by line. 语法格式:[ diff [选项] 源文件 新文件 > *.patch ] -a #将任何文档当做文本文档处理
-b #忽略空格造成的不同
-B #忽略空白行造成的不同
-I #忽略大小写造成的不同
-N #当比较目录时,若某个文件只在一个目录中,则另一个目录中视作空文件
-r #当比较目录时,递归比较子目录
-u #使用同一的输出格式 -----------------------------------------------------------------
说明: 生成补丁的例子 1.首先创建两个文件,分别写入以下内容,内容要不同,因为要生成补丁. [root@localhost ~]# cat old
hello world [root@localhost ~]# cat new
hello world welcome to lyshark blog 2.利用命令生成补丁文件 *.patch [root@localhost ~]# diff -Naur /root/old /root/new > lyshark.patch [root@localhost ~]# cat lyshark.patch
--- /root/old 2018-09-21 05:41:55.487052312 -0400
+++ /root/new 2018-09-21 05:42:10.581184526 -0400
@@ -1 +1,3 @@
hello world
+
+welcome to lyshark blog #最后,生成的lyshark.patch就是补丁文件了,我们可以使用下面的命令对old文件打补丁了.

◆patch 文本打入补丁◆

patch命令被用于为开放源代码软件安装补丁程序,让用户利用设置修补文件的方式,修改更新原始文件.如果一次仅修改一个文件,可直接在命令列中下达指令依序执行,如果配合修补文件的方式则能一次修补大批文件,这也是Linux系统核心的升级方法之一.

注意:精简模式下没有这个命令,需要执行 yum install -y patch 安装一下

[root@localhost ~]# patch --help
Usage: patch [OPTION]... [ORIGFILE [PATCHFILE]] 语法格式:[ patch [-pn] 旧文件 < *.patch ] -p:设置要剥离的目录层数
n:代表补丁文件中记录的文件old所在目录层数,用于更新old文件时匹配正确路径 -----------------------------------------------------------------
说明: 给old文件打补丁 [root@localhost ~]# ls
lyshark.patch new old [root@localhost ~]# patch -p2 old < lyshark.patch
patching file old [root@localhost ~]# cat old
hello world welcome to lyshark blog [root@localhost ~]# cat new
hello world welcome to lyshark blog

参考文献:Linux鸟哥私房菜,Linux运维之道

Linux正则与文本处理工具(10)的相关教程结束。

《Linux正则与文本处理工具(10).doc》

下载本文的Word格式文档,以方便收藏与打印。