shell对输入流的处理(awk详解)
shell脚本数据的处理:
1)数据检索:grep tr cut
2)数据处理:uniq sort tee paste xargs
之前的脚本中我们都是通过grep、cut、tr、uniq、sort等命令通过管道组合在一起将字符串检索出来,然后在通过shell中对应的运算得到结果,在数据检索过程中大家可能也体会到了其中的辛苦和蹩脚。没办法,会的就这么多,还需要完成任务。
缺点:复杂的命令组合
多次运算
上手难
解决办法:
介绍一个更加厉害的命令awk。他可以让大家从输出流中检索出自己需要的数据而不需要再向以前那样通过大量命令组合来完成,只需一个命令awk就能完成。并且还能够通过awk对数据进行处理,而不再需要额外的shell运算。
awk的应用场景
字符串截取
数据运算
比如内存使用率脚本
一、awk介绍
在日常计算机管理中,总会有很多数据输出到屏幕或者文件,这些输出包含了标准输出、标准错误输出。默认情况下,这些信息全部输出到默认输出设备—屏幕。然而,大量的数据输出中,只有一小部分是我们需要重点关注的,我们需要把我们需要的或者关注的这些信息过滤或者提取以备后续需要时调用。早先的学习中,我们学过使用grep来过滤这些数据,使用cut、tr命令提出某些字段,但是他们都不具备提取并处理数据的能力,都必须先过滤,再提取转存到变量,然后在通过变量提取去处理,比如:
内存使用率的统计步骤
1) 通过free -m提取出内存总量,赋值给变量 memory_totle
2)通过free -m提取出n内存使用量,赋值给变量memory_use
3)通过数学运算计算内存使用率
需要执行多步才能得到内存使用率,那么有没有一个命令能够集过滤、提取、运算为一体呢?当然,就是今天我要给大家介绍的命令:awk
平行命令还有gawk、pgawk、dgawk
awk是一种可以处理数据、产生格式化报表的语言,功能十分强大。awk 认为文件中的每一行是一条记录 记录与记录的分隔符为换行符,每一列是一个字段 字段与字段的分隔符默认是一个或多个空格或tab制表符.
awk的工作方式是读取数据,将每一行数据视为一条记录(record)每条记录以字段分隔符分成若干字段,然后输出各个字段的值.
总结一下:awk就是行编辑器,把数据进行截取,同时也可以对数据完成运算,处理和输出!!
(多少条记录相当于多少行,把列叫做字段)
[root@CentOs shell]# aaaa\nbbbb\ncccc\n(有三个回车符,说明有三行)
[root@CentOs shell]# aaaa bbbbb cccc(有两个空格,说明有三列)
二、awk基本用法
awk [options] '[BEGIN]{program}[END]' [FILENAME]
常用命令选项
-F fs 指定描绘一行中数据字段的文件分隔符 默认为空格
-f file 指定读取程序的文件名
-v var=value 定义awk程序中使用的变量和默认值
注意:awk 程序由左大括号和右大括号定义。 程序命令必须放置在两个大括号之间。由于awk命令行假定程序是单文本字符串,所以必须将程序包括在单引号内。
1)程序必须放在大括号内
2)程序必须要用单引号引起来
awk程序运行优先级是:
1)BEGIN: 在开始处理数据流之前执行,可选项
2)program: 如何处理数据流,必选项
3)END: 处理完数据流后执行,可选项
能够熟练使用awk对标准输出的行、列、字符串截取!
测试:
[root@CentOs shell]# cat data
1 the quick brown fox jumps over the lazy cat . dog
2 the quick brown fox jumps over the lazy cat . dog
3 the quick brown fox jumps over the lazy cat . dog
4 the quick brown fox jumps over the lazy cat . dog
5 the quick brown fox jumps over the lazy cat . dog
[root@CentOs shell]#
字段提取:提取一个文本中的一列数据并打印输出
字段相关内置变量
$0 表示整行文本
$1 表示文本行中的第一个数据字段(第一列)
$2 表示文本行中的第二个数据字段
$N 表示文本行中的第N个数据字段
$NF 表示文本行中的最后一个数据字段a
[root@CentOs shell]# awk '{print $0}' data
1 the quick brown fox jumps over the lazy cat . dog
2 the quick brown fox jumps over the lazy cat . dog
3 the quick brown fox jumps over the lazy cat . dog
4 the quick brown fox jumps over the lazy cat . dog
5 the quick brown fox jumps over the lazy cat . dog
[root@CentOs shell]#
[root@CentOs shell]# awk '{print $NF}' data
dog
dog
dog
dog
dog
[root@CentOs shell]#
[root@CentOs shell]# awk '{print $3}' data
quick
quick
quick
quick
quick
[root@CentOs shell]#
记录提取:提取一个文本中的一行并打印输出
记录的提取方法有两种:a、通过行号 b、通过正则匹配
记录相关内置变量
NR: 指定行号 number row
[root@CentOs shell]# awk 'NR==3{print $0}' data
3 the quick brown fox jumps over the lazy cat . dog
[root@CentOs shell]#
[root@CentOs shell]# awk -F ":" 'NR==1{print$1}' passwd (打印passwd文件中以“:”分割开的第一行第一列!)(文件不规律)
root
[root@CentOs shell]# cat passwd
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
……
[root@CentOs shell]# awk -F ":" 'NR==1{print $1,$3,$5}' passwd (打印多列,中间的逗号可以给两个数字之前留出距离,认为你这一列打印完毕!-F那里可以写成“-F:”)
root 0 root
[root@CentOs shell]# head -1 passwd
root:x:0:0:root:/root:/bin/bash
[root@CentOs shell]#
[root@CentOs shell]# awk -F ":" 'NR==1{print $1 "-" $3 "-" $5 }' passwd (搭配字符串使用)
root-0-root
[root@CentOs shell]#
[root@CentOs shell]# awk -F ":" 'NR==1{print "account: "$1 ,"UID: "$3 ,"DESC: "$5}' passwd
account: root UID: 0 DESC: root
[root@CentOs shell]#
-F: 指定字段与字段的分隔符
当输出的数据流字段格式不是awk默认的字段格式时,我们可以使用-F命令选项来重新定义数据流字段分隔符。
-f file: 如果awk命令是日常重复工作,而又没有太多变化,可以将程序写入文件,每次使用-f调用程序文件就好,方便,高效。
[root@CentOs shell]# cat abc
{print $1,$3,$NF}
[root@CentOs shell]# awk -f abc data
1 quick dog
2 quick dog
3 quick dog
4 quick dog
5 quick dog
-v 定义变量,既然作者写awk的时候就是按着语言去写的,那么语言中最重要的要素—变量肯定不能缺席,所以可以使用-v命令选项定义变量
[root@CentOs shell]# cat num
1
2
3
4
5
6
7
8
9
10
[root@CentOs shell]# awk -v 'sum=0' '{sum+=$1}END{print sum}' num
55
[root@CentOs shell]#
关于awk程序的执行优先级,BEGIN是优先级最高的代码块,是在执行PROGRAM之前执行的,不需要提供数据源,因为不涉及到任何数据的处理,也不依赖与PROGRAM代码块;PROGRAM是对数据流干什么,是必选代码块,也是默认代码块。所以在执行时必须提供数据源;END是处理完数据流后的操作,如果需要执行END代码块,就必须需要PROGRAM的支持,单个无法执行。
BEGIN:处理数据源之前干什么 不需要数据源就可以执行
PROGRAM: 对数据源干什么 【默认必须有】 需要数据源
END:处理完数据源后干什么 需要program 需要数据源
例如:
优先级展示:
[root@CentOs shell]# awk 'BEGIN{print "hello root!"}{print $0}END{print "Bye root!"}' data
hello root!
1 the quick brown fox jumps over the lazy cat . dog
2 the quick brown fox jumps over the lazy cat . dog
3 the quick brown fox jumps over the lazy cat . dog
4 the quick brown fox jumps over the lazy cat . dog
5 the quick brown fox jumps over the lazy cat . dog
Bye root!
[root@CentOs shell]#
BEGIN不需要数据源,可以直接执行:
[root@CentOs shell]# awk 'BEGIN{print "hello root!"}'
hello root!
[root@CentOs shell]#
PROGRAM和END没有数据源,无法执行成功:
[root@CentOs shell]# awk '{print "hello root!"}'
^C
[root@CentOs shell]# awk 'END{print "hello root!"}'
^C
[root@CentOs shell]#
awk是一门语言,那么就会符合语言的特性,除了可以定义变量外,还可以定义数组,还可以进行运算,流程控制,我们接下来看看吧。
定义变量:
[root@CentOs shell]# awk 'BEGIN{name="root";print name}'
root
[root@CentOs shell]#
[root@CentOs shell]# head -2 /proc/meminfo
MemTotal: 1862996 kB
MemFree: 507200 kB
[root@CentOs shell]# head -2 /proc/meminfo |awk 'NR==1{t=$2}NR==2{f=$2;print (t-f)*100/t "%"}'(计算内存使用率)
72.7626%
[root@CentOs shell]#
定义数组:
数组定义方式: 数组名[索引]=值
[root@CentOs shell]# awk 'BEGIN{array[1]="root";array[2]="18";print array[1],array[2]}'(注意:定义的两个数组之前用分号隔开!)
root 18
[root@CentOs shell]#
- 赋值运算 =
- 比较运算 > >= == < <= !=
- 数学运算 + - * / % ** ++ –
- 逻辑运算 && || !
- 匹配运算 ~ !~ 精确匹配 == !=
[root@CentOs shell]# awk 'BEGIN{name="root";print name}'
root
[root@CentOs shell]#
[root@CentOs shell]# head -2 /proc/meminfo |awk 'NR==1{t=$2}NR==2{f=$2;print (t-f)*100/t "%"}'(计算内存使用率)
72.7626%
[root@CentOs shell]#
如果比较的是字符串则按ascii编码顺序表比较。如果结果返回为 :真则用1表示,如果返回为假则用0表示
[root@CentOs shell]# awk 'BEGIN{print "a" >= "b"}'
0
[root@CentOs shell]#
[root@CentOs shell]# seq 1 10 > num
[root@CentOs shell]# cat num
1
2
3
4
5
6
7
8
9
10
[root@CentOs shell]# awk '$1>5{print $0}' num
6
7
8
9
10
[root@CentOs shell]# awk '$1<=3{print $0}' num
1
2
3
[root@CentOs shell]#
[root@CentOs shell]# awk 'BEGIN{print 100+3}'
103
[root@CentOs shell]# awk 'BEGIN{print 100-3}'
97
[root@CentOs shell]# awk 'BEGIN{print 100*3}'
300
[root@CentOs shell]# awk 'BEGIN{print 100/3}'
33.3333
[root@CentOs shell]# awk 'BEGIN{print 100%3}'
1
[root@CentOs shell]#
[root@CentOs shell]# awk -v 'count=0' 'BEGIN{count++;print count}'
1
[root@CentOs shell]# awk -v 'count=0' 'BEGIN{count--;print count}'
-1
[root@CentOs shell]#
与运算:真真为真,真假为假,假假为假
[root@CentOs shell]# awk 'BEGIN{print 100>=2 && 100>=3}'
1
[root@CentOs shell]# awk 'BEGIN{print 100>=2 && 100>=101}'
0
[root@CentOs shell]#
或运算:真真为真,真假为假,假假为假
[root@CentOs shell]# awk 'BEGIN{print 100>=2 || 100>=101}'
1
[root@CentOs shell]#
精确(不)匹配:
[root@CentOs shell]# awk -F: '$1=="root"{print $0}' passwd
root:x:0:0:root:/root:/bin/bash
[root@CentOs shell]# awk -F: '$1!="root"{print $0}' passwd
模糊匹配:
[root@CentOs shell]# awk -F: '$1 ~ "ro"{print $0}' passwd
root:x:0:0:root:/root:/bin/bash
chrony:x:998:996::/var/lib/chrony:/sbin/nologin
[root@CentOs shell]# awk -F: '$1 !~ "ro"{print $0}' passwd
变量 | 描述 |
---|---|
FIELDWIDTHS | 以空格分隔的数字列表,用空格定义每个数据字段的精确宽度 |
FS | 输入字段分隔符号 数据源的字段分隔符 -F |
OFS | 输出字段分隔符号 |
RS | 输入记录分隔符 |
ORS | 输出记录分隔符号 |
FIELDWIDTHS:重定义列宽并打印,注意不可以使用$0打印所有,因为$0是打印本行全内容,不会打印你定义的字段
fieldwidths
(1)FIELDWIDTHS:以空格分隔的数字列表,用空格定义每个数据字段的精确宽度
[root@CentOs shell]# awk 'BEGIN{FIELDWIDTHS="5 2 8"}NR==1{print $1,$2,$3}' passwd
root: x: 0:0:root
[root@CentOs shell]#
(2)FS:输入字段分隔符号 数据源的字段分隔符 -F
[root@CentOs shell]# awk 'BEGIN{FS=":"}$1 ~ "ro"{print $0}' passwd
root:x:0:0:root:/root:/bin/bash
chrony:x:998:996::/var/lib/chrony:/sbin/nologin
[root@CentOs shell]#
(3)OFS:输出字段分隔符号
[root@CentOs shell]# awk 'BEGIN{FS=":";OFS="-"}$1 == "root"{print $1,$3,$5}' passwd
root-0-root
[root@CentOs shell]# awk -F: 'BEGIN{OFS="-"}$1=="root"{print $1$3$5}' passwd (两条语句对比一下,发现没有中间那个逗号,结果也是不一样的)
root0root
(4)RS:输入记录分隔符
[root@CentOs shell]# cat num
1
2
3
4
5
6
7
8
9
10
[root@CentOs shell]# awk 'BEGIN{RS=""}{print $1,$2,$3}' num (RS指定行与行之间的分割符不再是回车,而是空)
1 2 3
[root@CentOs shell]# awk 'BEGIN{RS="";OFS="\n"}{print $1,$2,$3}' num
1
2
3
[root@CentOs shell]#
(5)ORS:输出记录分隔符号
[root@CentOs shell]# awk 'BEGIN{RS="";ORS="$$$"}{print $1,$2,$3}' num
1 2 3$$$[root@CentOs shell]#
(1)if判断语句
[root@CentOs shell]# awk '{if($1>5)print $0}' num
6
7
8
9
10
[root@CentOs shell]# awk '{(或者这样写!)
> if($1>5)
> print $0
> }' num
6
7
8
9
10
[root@CentOs shell]#
[root@CentOs shell]# awk '{
if($1<5)
print $1*5
else
print $1/5
}' num
5
10
15
20
1
1.2
1.4
1.6
1.8
2
[root@CentOs shell]# awk '{if($1<5)print $1*5;else print $1/5}' num
5
10
15
20
1
1.2
1.4
1.6
1.8
2
[root@CentOs shell]#
(2)for循环语句
[root@CentOs shell]# cat num2
60 50 100
150 30 10
70 100 40
[root@CentOs shell]# awk '{
> for (i=1;i<=3;i++)
> sum+=$1
> print sum
> }' num2
180
630
840
[root@CentOs shell]# awk '{
for (i=1;i<=3;i++)
sum+=$i
print sum
}' num2
210
400
610
[root@CentOs shell]# awk '{sum=0;for(i=1;i<=3;i++)sum+=$i;print sum}' num2
210
190
210
[root@CentOs shell]#
[root@CentOs shell]# awk '{(while循环)
> sum=0
> i=1
> while (i<4){
> sum+=$i
> i++}
> print sum
> }' num2
210
190
210
[root@CentOs shell]# awk '{sum=0;i=1;while(i<4){sum+=$i;i++}print sum}' num2
210
190
210
[root@CentOs shell]#
[root@CentOs shell]# awk '{(do while)
> sum=1
> i=1
> do{
> sum+=$i
> i++
> }while (sum<150)
> print sum
> }' num2
211
151
171
[root@CentOs shell]# awk '{sum=1;i=1;do{sum+=$i;i++}while (sum<150)print sum}' num2
211
151
171
[root@CentOs shell]#
(3)循环控制语句
[root@CentOs shell]# awk '{
> sum=0
> i=1
> while (i<4){
> sum+=$i
> if (sum>150)
> break
> i++}
> print sum
> }' num2
210
180
170
[root@CentOs shell]#
[root@CentOs shell]# cat data
1 the quick brown fox jumps over the lazy cat . dog
2 the quick brown fox jumps over the lazy cat . dog
3 the quick brown fox jumps over the lazy cat . dog
4 the quick brown fox jumps over the lazy cat . dog
5 the quick brown fox jumps over the lazy cat . dog
[root@CentOs shell]# awk 'END{print NR}' data
5
[root@CentOs shell]#
[root@CentOs shell]# awk 'END {print $0}' data
5 the quick brown fox jumps over the lazy cat . dog
[root@CentOs shell]#
[root@CentOs shell]# awk 'END {print NF}' data
12
[root@CentOs shell]#