前言
在日常工作学习中,不免经常要对文本文件(例如日志文件)进行处理工作,包括但不限于分割、查找、替换、删除等操作,Shell
中有没有相应的命令供我们使用呢?那么本篇文章,我们就一起来学习下吧!
cut
grep
命令可以查找文件中符合条件的行,cut
命令则可以根据分隔符,提取行中的列,默认分隔符为 TAB
。
1
2
3
4
5
6
7
8
9
|
# cut [选项] 文件名
选项
-f 列号: 提取第几列
-d 分隔符: 按照指定分隔符分割列
# cut默认分隔符是TAB
|
测试一下这个命令就容易理解了。我们首先新建一个文件,然后使用 TAB
分隔,然后使用 cut
命令提取列。
示例一
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
[root@VM-0-5-centos ~]# vim student.txt
ID NAME Gender MARK
1 zs M 98
2 ls F 99
# 1. 提取第二列
[root@VM-0-5-centos ~]# cut -f 2 student.txt
NAME
zs
ls
# 2. 提取第二列和第四列
[root@VM-0-5-centos ~]# cut -f 2,4 student.txt
NAME MARK
zs 98
ls 99
# 3. 测试使用分隔符提取列
# 3.1 这是我们需要处理的文本
[root@VM-0-5-centos ~]# cat /etc/passwd | grep /bin/bash | grep -v root
lifelmy❌1000:1000::/home/lifelmy:/bin/bash
user1❌1001:1001::/home/user1:/bin/bash
user3❌1002:1003::/home/user3:/bin/bash
# 3.2 提取第一列
[root@VM-0-5-centos ~]# cat /etc/passwd | grep /bin/bash | grep -v root | cut -f 1 -d :
lifelmy
user1
user3
|
从上面的例子我们可以看出,cut
命令可以提取行中的列,但是 cut
命令有一个缺点,就是分隔符的长度必须一致。如果列与列之间,有的是一个空格符,有的是两个空格符的话,cut
命令就不能正确划分列了。
示例二
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
[root@VM-0-5-centos ~]# df -h
文件系统 容量 已用 可用 已用% 挂载点
devtmpfs 909M 0 909M 0% /dev
tmpfs 919M 24K 919M 1% /dev/shm
tmpfs 919M 540K 919M 1% /run
tmpfs 919M 0 919M 0% /sys/fs/cgroup
/dev/vda1 50G 5.6G 42G 12% /
tmpfs 184M 0 184M 0% /run/user/0
# 使用 空格 作为分隔符,查出第一列
[root@VM-0-5-centos ~]# df -h | cut -f 1 -d " "
文件系统
devtmpfs
tmpfs
tmpfs
tmpfs
/dev/vda1
tmpfs
# 但是查第二列的时候,得到的都是空格,不是我们想要的"容量"那一列
[root@VM-0-5-centos ~]# df -h | cut -f 2 -d " "
|
awk
从上面的示例二可以看出,cut
还是有些缺点的,那么这个问题有没有其他命令可以解决呢,这就轮到我们的 awk
命令出场了。在详细介绍 awk
命令前,我们先来看一下标准输出命令。
在awk命令的输出中支持 print
和 printf
命令
printf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
printf "输出类型 输出格式" 输出内容
[输出类型]:
%ns: 输出字符串,n表示输出几个字符
%ni: 输出整数,n表示输出几个数字
%m.nf: 输出浮点数,m、n分别表示输出的位数以及其中的小数位数. %8.2f表示共输出8位数,其中2位是小数,6位整数
[输出格式]:
\a: 输出警告声音
\b: 输出退格键
\f: 清除屏幕
\n: 换行
\r: 回车
\t: 水平输出制表符
\v: 垂直输出制表符
|
示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
# %-5s 格式为左对齐且宽度为5的字符串代替('-'表示左对齐),不使用则默认右对齐。
# %-4.2f 格式为左对齐宽度为4,保留两位小数。
[root@VM-0-5-centos ~]# printf "%-5s %-10s %-4s\n" NO Name Mark
NO Name Mark
[root@VM-0-5-centos ~]# printf "%-5s %-10s %-4.2f\n" 01 Tom 90.3456
01 Tom 90.35
[root@VM-0-5-centos ~]# printf "%-5s %-10s %-4.2f\n" 02 Jack 89.2345
02 Jack 89.23
[root@VM-0-5-centos ~]# printf "%-5s %-10s %-4.2f\n" 03 Jeff 98.4323
03 Jeff 98.43
[root@VM-0-5-centos ~]# printf '%d %d %d\n' 12 34 56
12 34 56
|
awk
是一种编程语言,用于在linux/unix下对文本和数据进行处理。数据可以来自标准输入(stdin)、一个或多个文件,或其它命令的输出。它支持用户自定义函数和动态正则表达式等先进功能,是linux/unix下的一个强大编程工具。它在命令行中使用,但更多是作为脚本来使用。awk有很多内建的功能,比如数组、函数等,这是它和C语言的相同之处,灵活性是awk最大的优势。
1
2
3
4
5
6
7
8
9
10
11
|
awk 'BEGIN{动作} 条件1{动作1} 条件2{动作2} END{动作}' file
条件(Pattern):
一般使用关系表达式作为条件
x>10
x<=10
动作(Action):
格式化输出
流程控制语句
|
一个awk脚本由三部分组成:BEGIN语句块、能够使用条件匹配的通用语句块、END语句块3部分组成,这三个部分是可选的。任意一个部分都可以不出现在脚本中。
BEGIN条件,会在读取文件第一行之前执行相应的动作;END条件,读取完文件后执行。
测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
# 1. 在处理第一行文件时先执行BEGIN对应的动作,然后无条件对每一行使用默认的空格分隔后取出第5列,最后执行 END 对应的动作
[root@VM-0-5-centos ~]# df -h | awk 'BEGIN{printf "this is begin \n"} {printf $5 "\n"} END{printf "this is end \n"}'
this is begin
已用%
0%
1%
1%
0%
12%
0%
this is end
# 2. 在处理第一行文件前赋值 i=0,然后每处理一行就将i加一,最后输出i的值
[root@VM-0-5-centos ~]# df -h | awk 'BEGIN{ i=0 } { i++ } END{ print i }'
7
# 3. 将1、2两个例子结合起来
[root@VM-0-5-centos ~]# df -h | awk 'BEGIN{i=0 ;printf "start \n"} {printf $5 "\t"} {i++; printf i "\n"} END{printf "end\n"}'
start
已用% 1
0% 2
1% 3
1% 4
0% 5
12% 6
0% 7
end
|
由于 awk
命令的默认分隔符是空格(没有数量限制),因此对于分隔符不是空格的文件来说,BEGIN
的主要作用就是在处理文件前设置分隔符,其中FS
用来设置分隔符。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
# 要处理的数据
[root@VM-0-5-centos ~]# cat /etc/passwd | grep syslog
syslog❌996:994::/home/syslog:/bin/false
# 使用 BEGIN 设置分隔符为":",然后输出第三列数据
[root@VM-0-5-centos ~]# cat /etc/passwd | grep syslog | awk 'BEGIN {FS=":"} {printf $3 "\n"}'
996
# 使用关系运算符条件
[root@VM-0-5-centos ~]# cat /etc/passwd | awk 'BEGIN {FS=":"} $3>995{printf $3 "\n"}'
999
998
997
996
1000
1001
1002
|
下面给出一个具体问题,思考下你会怎么处理。上面我们已经使用过了 df -h
命令,该命令是用于查看Linux的磁盘占用情况,我们想要设置个定时任务,每天去检查某个磁盘的占用情况,如果磁盘占用比例到了80%,就发送邮件报警。这里我们只处理第一步,就是判断磁盘占用情况是否超出了设置的阈值。
脚本文件
1
2
3
4
5
6
7
8
|
#!/bin/bash
read -p 'please input rate: ' r
rate=$(df -h | grep 'vda1'| awk '{printf $5}' | cut -d '%' -f 1)
if [ $rate -gt $r ]
then
echo 'alarm'
fi
|
测试
1
2
3
4
5
6
7
|
[root@VM-0-5-centos ~]# chmod 755 test.sh
[root@VM-0-5-centos ~]# ./test.sh
please input rate: 20
[root@VM-0-5-centos ~]# ./test.sh
please input rate: 10
alarm
|
sed
sed
是一种几乎包括所有unix平台(包括Linux)的轻量级流编辑器,它是文本处理中非常重要的工具,能够完美的配合正则表达式使用,功能不同凡响。
处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用 sed
命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有改变,除非你使用重定向存储输出。sed
主要用来自动编辑一个或多个文件;简化对文件的反复操作;编写转换程序等。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
# sed [选项] '动作' 文件名
选项:
-n 一般sed命令会把所有数据都输出到屏幕,如果加入此选择,则只会把经过sed命令处理的行输出到屏幕
-e 允许对输入数据应用多条sed命令编辑
-i 用sed的修改结果直接修改读取数据的文件,而不是由屏幕输出
动作:
a: 追加,在当前行后添加一行或多行。添加多行时,除最后一行外,每行末尾需要用'\' 代表数据未完结
c: 行替换,用'c' 后面的字符串替换原数据行,替换多行时,除最后一行外,每行末尾用'\' 表示数据未完结
i: 插入,在当前行前插入一行或多行。插入多行时,除最后一行外,每行末尾用'\' 表示数据未完结
d: 删除,删除指定的行
p: 打印,输出指定的行
s: 字符串替换,用一个字符串替换另一个字符串。格式为 "第几行s/旧字串/新字串/g",使用 '/g'表示替换该行中的所有匹配 (与vim类似)
|
测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
# 要处理的文件
[root@VM-0-5-centos ~]# cat student.txt
ID NAME Gender MARK
1 zs M 98
2 ls F 99
# 打印输出第2行,默认不仅会输出指定的行,还会输出原有文件中的所有行
[root@VM-0-5-centos ~]# sed '2p' student.txt
ID NAME Gender MARK
1 zs M 98
1 zs M 98
2 ls F 99
# -n,只输出sed处理的数据
[root@VM-0-5-centos ~]# sed -n '2p' student.txt
1 zs M 98
# 删除第2行的数据,但不修改原文件 (在缓冲区修改的)
[root@VM-0-5-centos ~]# sed '2d' student.txt
ID NAME Gender MARK
2 ls F 99
[root@VM-0-5-centos ~]# cat student.txt
ID NAME Gender MARK
1 zs M 98
2 ls F 99
# 在第3行后追加一条数据,不修改原数据 (在缓冲区修改的)
[root@VM-0-5-centos ~]# sed '3a hello world' student.txt
ID NAME Gender MARK
1 zs M 98
2 ls F 99
hello world
# 在第3行前插入一条数据,不修改原数据 (在缓冲区修改的)
[root@VM-0-5-centos ~]# sed '3i hello world' student.txt
ID NAME Gender MARK
1 zs M 98
hello world
2 ls F 99
# 替换第3行的数据,不修改原数据 (在缓冲区修改的)
[root@VM-0-5-centos ~]# sed '3c hello world' student.txt
ID NAME Gender MARK
1 zs M 98
hello world
# 替换字符串字段,不修改原数据 (在缓冲区修改的)
[root@VM-0-5-centos ~]# sed '2s/98/100/g' student.txt
ID NAME Gender MARK
1 zs M 100
2 ls F 99
# 字符串替换,直接修改文件
[root@VM-0-5-centos ~]# sed -i '2s/98/100/g' student.txt
[root@VM-0-5-centos ~]# cat student.txt
ID NAME Gender MARK
1 zs M 100
2 ls F 99
# 替换多处
[root@VM-0-5-centos ~]# sed -i '2s/M/F/; 2s/zs/ww/' student.txt
[root@VM-0-5-centos ~]# cat student.txt
ID NAME Gender MARK
1 ww F 100
2 ls F 99
|
总结
本篇文章一共学习了三个命令:
-
cut:用于提取一行文本中的列,默认分隔符是 TAB
,同时也可以指定分隔符。如果分隔符是空格,需要保证每列之间的空格数相同;
-
awk:一种编程语言,功能强大,支持数组、函数等,类似 C语言;
-
sed:功能强大的流式文本编辑器,可以很方便的处理文件中的每一行数据。
PS:本文只是简单的介绍了命令的常用部分,如果想要更加深入了解,可以去这里学习。
更多
微信公众号:CodePlayer