常见的数据分析:
数据数据收集:负责数据的收集
数据清洗:负责数据的筛选
数据分析:数据运算、整理
数据展示:图表或表格方式输出结果
shell脚本数据的处理 :
之前的脚本都是通过grep、tr、cut、uniq、sort、tee、paste、xargs等命令通过管道组合在一起将字符串检索出来的,然后通过shell中对应的运算得到结果,在数据检索过程中操作比较繁琐
awk介绍:
在日常计算机管理中,总有很多数据输出到屏幕或文件中,这些数据包含了标准输出、标准错误输出。默认情况下,这些信息全部输出到默认输出设备(屏幕)。这些数据只有一小部分是需要重点关注的。我们要把关注的信息通过过滤或者提取以备后续调用。早先的学习中(grep来过滤数据、cut和tr提出某些字段),但它们都不具备提取并处理数据的能力,都必须先过滤,再提取转存到内存中,然后通过变量提取去处理。比如:
内存使用率的统计步骤:
通过free -m 提取成内存总量,赋值给变量memory_totle
通过free -m 提取出n内存使用量,赋值给变量memory_use
通过数学运算计算内存使用率
操作过于繁琐,需要执行多步才能得到内存使用率,而
awk命令
能够集过滤、提取、运算为一体
awk命令可以从输出流中检索自己需要的数据,而不需要向以前一样通过大量命令组合检索。只需要一个awk命令就能完成,并且还能够通过awk对数据进行处理,而不需要额外的shell运算
awk命令是一种可以处理数据、产生格式化报表的语言,功能十分强大。awk认为文件中的每一行都是一条记录
,记录与记录的分隔符为换行符
;每一列都是一个字段
,字段与字段的分隔符默认是一个或多个空格或tab制表符
awk的工作是读取数据,将每一行数据视为一条记录,每条记录以字段分隔符划分若干个字段,然后输出各字段的值
awk的平行命令还有:gawk、pgawk、dgawk
总结: awk 是行编辑器,负责数据的截取和处理
语法格式:
# awk [option] [BEGIN]{pogram}[END][filename]
常用options选项:
-F :指定描绘一行中数据字段的文件分隔符,默认为空格(即指定行的分隔符)
-f file:指定读取程序的文件名,就是将awk的命令放到文本中用-f选项调用
-v var=value:定义awk程序中使用的变量和默认值
#l 注意:awk程序脚本由左大括号和有大括号定义。脚本命令必须放置在两个大括号之间。
#l 由于awk命令假定脚本是单文本字符串,所以必须将脚本包括在单引号内
#awk程序运行优先级是:
1)"BENGIN":在开始处理数据流前执行,可选项
2)"program":如何处理数据流,必选项
3) "END":处理完数据流后执行,可选项
字段提取: 提取一个文本中的一列数据并打印输出
用逗号,
分隔字段内置变量打印
字段相关内置变量:
$0
:表示整行文本$1
:表示文本行中的第一个数据字段$2
:表示文本行中的第二个数据字段$N
:表示文本行中的第N个数据字段$NF
:表示文本行中的最后一个数据字段[root@server ~]# cat awk_column
1 This little sister is my dish
2 This little sister is my dish
3 This little sister is my dish
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
#l 提取列(第一列)
[root@server ~]# awk '{print $1}' awk_column
1
2
3
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
#l 提取列(第二列)
[root@server ~]# awk '{print $2}' awk_column
This
This
This
//这是打印的空行
//这是打印的空行
#l 指定分隔符提取列(第二列)
[root@server ~]# awk -F ":" '{print $2}' awk_column
//这是打印的空行
//这是打印的空行
//这是打印的空行
x
x
#l 打印第1,3列数据
[root@server ~]# awk '{print $1,$3}' awk_column
1 little
2 little
3 little
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
#l 打印第1列和最后一列数据
[root@server ~]# awk '{print $1,$NF}' awk_column => 第四五行只有一列,所以第一列和最后一列都是它自身
1 dish
2 dish
3 dish
root:x:0:0:root:/root:/bin/bash root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin bin:x:1:1:bin:/bin:/sbin/nologin
记录提取: 提取一个文本的一行并打印输出
用逗号,
分隔行号打印;NR==1,NR==3
:打印1至3行
用冒号;
分割行号awk程序;NR==1,NR==3
:打印1和3行
记录的提取方法有两种:通过行号提取
和通过正则匹配
记录相关内置变量:
NR==N
:指定行号N[root@server ~]# cat awk_column
1 This little sister is my dish
2 This little sister is my dish
3 This little sister is my dish
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
#l 提取行全部数据(第四行)
[root@server ~]# awk 'NR==4{print $0}' awk_column
root:x:0:0:root:/root:/bin/bash
#l 提取第四行第5个字段,-F 指定分隔符 ":"
[root@server ~]# awk -F ":" 'NR==4{print $5}' awk_column
root
#l 同时打印多列(第二行3,4列)
[root@server ~]# awk 'NR==2{print $3,$4}' awk_column
little sister
#L 打印多行,同时可以指定输出列的位置。(NR==1,NR==3:代表1到3行) => , 逗号分隔行号
[root@server ~]# awk 'NR==1,NR==3{print $6,$3,$4}' awk_column
my little sister
my little sister
my little sister
#l 打印多行,分割awk程序 => ; 冒号分割
[root@server ~]# awk 'NR==3;NR==3{print $1,$6,$3,$4}' awk_column
3 This little sister is my dish
3 my little sister
关于awk程序执行的优先级,BEGIN
是优先级最高的代码块,是在执行program之前执行,不需要提供数据源,因为不涉及到任何数据的处理,也不依赖于program代码块
program
是对数据流操作,是必选代码块,也是默认代码块。所以在执行时必须提供数据源
END
是处理完数据流后的操作,如果需要执行END代码块,就必须需要program的支持,单个无法执行
BEGIN 说明:
#l 不需要数据源就可以执行:
[root@server ~]# awk 'BEGIN{print "hello"}'
hello
程序优先级说明:
[root@server ~]# awk 'END{print "end========"}{print $0}BEGIN{print "start========"}' awk_column
start========
1 This little sister is my dish
2 This little sister is my dish
3 This little sister is my dish
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
end========
#l 备注:会优先执行BEGIN的内容,然后是program,再然后是END的内容
程序优先级总结:
BEGIN
:处理数据源之前干什么,不需要数据源就可以执行
program
:对数据源干什么,需要数据源(截取和运算)
END
:对数据源处理后干什么,需要数据源
awk 是一本语言,那么就会符合语言的特性,除了可以定义变量外、还可以定义数组、还可以进行计算、还可以流程控制
-F
指定的分隔符删除打印,
隔开-F
不会使用空格分隔符#l 以冒号:为分割符打印1,3,最后一个字段,默认以空格为分隔符打印
[root@server ~]# awk -F ":" 'NR==1{print $1,$5,$NF}' /etc/passwd
root root /bin/bash
#l 可以通过指定字符串打印
[root@server ~]# awk -F ":" 'NR==1{print $1"-"$5"-"$NF}' /etc/passwd
root-root-/bin/bash
#l 没有逗号,不隔开
[root@server ~]# awk -F ":" 'NR==1{print "uid:"$1"gid:"$5"shell:"$NF}' /etc/passwd
uid:rootgid:rootshell:/bin/bash
#l 逗号隔开列内置变量,打印列时有空格分隔每个字段
[root@server ~]# awk -F ":" 'NR==1{print "uid:"$1,"gid:"$5,"shell:"$NF}' /etc/passwd
uid:root gid:root shell:/bin/bash
在程序里面定义变量需要用冒号;
分割
#l 用 -v 选项定义一个变量
[root@server ~]# awk -v name='coco' 'BEGIN{print name}'
coco
#l 在BEGIN里面定义变量
[root@server ~]# awk 'BEGIN{name="liwen";print name}'
liwen
#l 对数据源中的数据赋值给变量(计算totl-free得到使用的内存量)
[root@server ~]# head -4 /proc/meminfo => 内存会发生变化
MemTotal: 1863252 kB
MemFree: 164712 kB
MemAvailable: 555936 kB
Buffers: 0 kB
[root@server ~]# awk 'NR==1{t=$2}NR==2{f=$2;print t,f}' /proc/meminfo
1863252 161420
[root@server ~]# awk 'NR==1{t=$2}NR==2{f=$2;print (t-f)}' /proc/meminfo => 总体差不多,因为内存是动态的
1701628
数组定义方式:数组名[索引]=值
;
分割#l 在BEGIN里面定义一个数组,取名a
[root@server ~]# awk 'BEGIN{a[0]="干啥子";a[1]=5;print a[0],a[1]}'
干啥子 5
#l 从数据源中将数据取出来赋值给数组,取名b
[root@server ~]# cat array
1 This little sister is my dish
2 This little sister is my dish
[root@server ~]# awk 'NR==2{b[0]=$1;b[1]=$4}NR==2{b[3]=$NF;print b[0],b[1],b[3]}' array
2 sister dish
文件内容:
[root@server ~]# cat num
1
2
3
4
5
6
7
8
9
10
aaa
awk 比较运算:(需要数据源的比较方式):
#l 大于 >
[root@server ~]# awk '$1>7{print $0}' num
8
9
10
aaa
#l 大于等于 >=
[root@server ~]# awk '$1>=7{print $0}' num
7
8
9
10
aaa
#l 等于 == (注意不要和一个等于赋值搅浑)
[root@server ~]# awk '$1==3{print $0}' num
3
#l 小于 < (aaa不见了)
[root@server ~]# awk '$1<3{print $0}' num
1
2
#l 小于等于 <=
[root@server ~]# awk '$1<=3{print $0}' num
1
2
3
#l 不等于 !=
[root@server ~]# awk '$1!="aaa"{print $0}' num
1
2
3
4
5
6
7
8
9
10
#l 赋值 =
[root@server ~]# awk '$1="再见"{print $0}' num
再见
再见
...全部都改变
awk 比较运算:(不需要数据源的比较方式):
#l 小于
[root@server awk]# awk 'BEGIN{print 3<4}' 真
1
[root@server awk]# awk 'BEGIN{print 4<3}' 假
0
#l 大于,注意:一个大于相当于覆盖输入 无效
[root@server awk]# awk 'BEGIN{print 4>3}'
[root@server awk]# ll
total 4
-rw-r--r-- 1 root root 2 Jun 5 01:09 3
[root@server awk]# cat 3
4
#l 大于等于
[root@server awk]# awk 'BEGIN{print 3>=1}' 真
1
[root@server awk]# awk 'BEGIN{print 1>=3}' 假
0
#l 小于等于
[root@server awk]# awk 'BEGIN{print 4<=5}' 真
1
[root@server awk]# awk 'BEGIN{print 5<=4}' 假
0
#l 等于等于
[root@server awk]# awk 'BEGIN{print 4==4}' 真
1
[root@server awk]# awk 'BEGIN{print 4==3}' 假
0
#l 不等于
[root@server nmap]# awk 'BEGIN{print 4!=3}' 真
1
[root@server nmap]# awk 'BEGIN{print 4!=4}' 假
0
#l 备注:一个等于(=)会报错,大于(>)覆盖输入
[root@server nmap]# awk 'BEGIN{print 2+3}'
5
[root@server nmap]# awk 'BEGIN{print 2-3}'
-1
[root@server nmap]# awk 'BEGIN{print 2*3}'
6
[root@server ~]# awk 'BEGIN{print 100/3}'
33.3333
[root@server ~]# awk 'BEGIN{print 1/10000}'
0.0001
[root@server ~]# awk 'BEGIN{print 1/100000}'
1e-05
[root@server nmap]# awk 'BEGIN{print 4%3}'
1
[root@server nmap]# awk 'BEGIN{print 2**3}'
8
[root@server nmap]# awk 'BEGIN{print (10.5+5.5)*2/4-1}'
7
#l ++ +=
[root@server ~]# awk 'BEGIN{a=1;print ++a}'
2
[root@server ~]# awk 'BEGIN{a=1;a+=1;print a}'
2
#l -- -=
[root@server ~]# awk 'BEGIN{a=10;print --a}'
9
[root@server ~]# awk 'BEGIN{a=10;;a-=1;print a}'
9
#L 精确匹配: ==
[root@server nmap]# awk -F ":" '$1=="root"{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
[root@server ~]# awk -F ":" '$1=="ro"{print $0}' /etc/passwd
ro:x:1002:1002::/home/ro:/bin/bash
#l 精确不匹配
[root@server ~]# awk -F ":" '$1!="root"{print $0}' /etc/passwd
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
....
#l 模糊匹配:字符为 ~ => 即第一列有ro字符的匹配出来
[root@server ~]# awk -F: '$1~"ro"{print $0}' /etc/passwd
"ro"ot:x:0:0:root:/root:/bin/bash
ch"ro"ny:x:992:987::/var/lib/chrony:/sbin/nologin
set"ro"ubleshoot:x:989:983::/var/lib/setroubleshoot:/sbin/nologin
ro:x:1002:1002::/home/ro:/bin/bash
[root@server ~]# awk -F: '$1~"^ro"{print $0}' /etc/passwd => 加入正则
root:x:0:0:root:/root:/bin/bash
ro:x:1002:1002::/home/ro:/bin/bash
#l 模糊不匹配:字符为 !~ => 即第一列有ro字符的不匹配
[root@server ~]# awk -F: '$1!~"ro"{print $0}' /etc/passwd
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
...
#l 逻辑匹配:||,匹配第一列为root和lisi行
[root@server ~]# awk -F ":" '$1=="root"||$1=="lisi"{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
lisi:x:1001:1001::/home/lisi:/bin/bash
#l 与逻辑运算:全真为真,一假永假
[root@server ~]# awk 'BEGIN{print 2>=1 && 3>=1}' 真
1
[root@server ~]# awk 'BEGIN{print 2>=1 && 3<1}' 假
0
#l 与逻辑运算:一真永真,全假为假
[root@server ~]# awk 'BEGIN{print 2>=1 || 3<1}' 真
1
[root@server ~]# awk 'BEGIN{print 2<=1 || 3<1}' 假
0
变量 | 描述 |
---|---|
FIELDWIDTHS |
以空格分隔的数字列表,用空格定义每个数据字段的精确宽度 |
FS |
输入字段分隔符号,与-F命令类似(数据源的字段分隔符) |
OFS |
输出字段分隔符号,打印时不能打印$0 |
RS |
输入记录分隔符号,默认是回车 |
ORS |
输出记录分隔符号 ,默认是回车,打印时不能打印$0 |
重新定义列宽并打印,注意不可以使用$0
打印所有,因为$0
是打印本行所有内容,不会打印你定义的字段
在BEGIN
中定义
FIELDWIDTHS
=“列宽值 列宽值 …”,列宽值代表打印出来的字符数量
如果原文本本来就有空格,这个空格也算一个字符被重新定义
[root@server ~]# awk 'NR==1{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
#l 表示第一列$1的字宽为5,第二列$2的字宽为2,第三列$3的字宽为8。本行没有定义列宽是不打印的
[root@server ~]# awk 'BEGIN{FIELDWIDTHS="5 2 8"}NR==1{print $1,$2,$3}' /etc/passwd
root: x: 0:0:root
#l 提取1到3行,重新定义列宽
[root@server ~]# awk 'BEGIN{FIELDWIDTHS="3 5 4"}NR==1,NR==3{print $1,$2,$3}' /etc/passwd
roo t:x:0 :0:r
bin :x:1: 1:bi
dae mon:x :2:2
#l 原文本有空格
[root@server ~]# awk 'BEGIN{FIELDWIDTHS="8 5 4 "}NR==1,NR==3{print $1,$2,$3}' sed.txt
' 1 This ' "littl" 'e si'
2 This littl e si
3 This littl e si
备注:引号包围的为一个字段
FS
:处理数据源前,指定数据源中字段分隔符,类似命令选项-F
OFS
:指定输出到屏幕后字段的分隔符
通过指定FS
输入分隔符,删除文本的指定分隔符;然后指定OFS
输出分隔符替换输出分隔符输出
FS;OFS
:以分号隔开
#l 指定输入分隔符,打印 第一行和第三行中,以FS=":"指定的分隔符打印1,3,最后一列字段(与-F:一致)
[root@server ~]# awk 'BEGIN{FS=":"}NR==1||NR==3{print $1,$3,$NF}' /etc/passwd
root 0 /bin/bash
daemon 2 /sbin/nologin
[root@server ~]# awk -F: 'NR==1,NR==3{print $1,$3,$NF}' /etc/passwd
root 0 /bin/bash
bin 1 /sbin/nologin
daemon 2 /sbin/nologin
#l OFS替换FS输出分隔符
[root@server ~]# awk 'BEGIN{FS=":";OFS=" - "}NR==1{print $1,$3,$NF}' /etc/passwd
root - 0 - /bin/bash
RS
:指定记录的分隔符#l RS="",表示告诉文件它的行分隔符不在是回车,而是空行,那么文件全部内容都在一行
[root@server ~]# awk 'BEGIN{RS=""}{print $1}' num
1
[root@server ~]# awk 'BEGIN{RS=""}{print $1,$2,$3,$4,$5,$6,$7,$8,$9,$10}' num
1 2 3 4 5 6 7 8 9 10
#l ORS=" ",表示将行的输出分隔符替换成空格,由原来的回车变成空格,
#l 所以将第一列的数据以空格分隔一行输出
[root@server ~]# awk 'BEGIN{ORS=" "}{print $1}' num
1 2 3 4 5 6 7 8 9 10 aaa [root@server ~]#
循环时文件中每一行被awk调用时都是重新开始循环的。
awk 单if
语句:
[root@server ~]# awk '{if ($1>7) print $0}' num
8
9
10
[root@server ~]# awk '{
> if ($1<4)
> print $1
> }' num
1
2
3
awk if..else
语句:
[root@server ~]# awk '{if ($1>7) print $1/3 ;else print $1*2}' num
2
4
6
8
10
12
14
2.66667
3
3.33333
[root@server ~]# awk '{
if ($1==10)
print $1/10
else
print ($1+100)
}' num
101
102
103
104
105
106
107
108
109
1
awk for循环
说明:
[root@server ~]# cat num2
123 223 456
122 544 23
112 33 45
#l 没有事先定义变量sum的值,得出每行的数据是累前面所有行的总和和
awk: cmd. line:2: ^ syntax error
[root@server ~]# awk '{
for (i=1;i<4;i++){
sum+=$i
}
print sum
}' num2
802
1491
1681
解释:因为变量sum是在for循环中定义的,当第一行for循环结束后,输出sum,这时的sun是第一行的和;
然后文件第二行被awk编辑,变量sum是上一行循环后的值,然后第二行的列数据继续往sum上加,
输出sum,这时的sum是第一行和第二行的和;然后以此类推,第三行输出的变量sum就是前三行的和
#l 利用awk for 循环计算num2中每一行的和
[root@server ~]# awk '{
> sum=0
> for (i=1;i<4;i++){
> sum+=$i
> }
> print sum
> }' num2
802
689
190
#l 备注:因为在循环外定义了sum=0,下一行进入循环就不会受到上一行循环变量sum的影响,输出值就是每行的和
[root@server ~]# awk '{
> sum=0
> i=1
> while (i<4) {
> sum+=$i
> i++
> }
> print sum
> }' num2
802
689
190
[root@server ~]# awk '{
> sum=0
> i=1
> do {
> sum+=$i
> i++
> }
> while (i<4)
> print sum
> }' num2
802
689
190
break 在for循环中跳出循环说明:
[root@server ~]# awk '{
> sum=0
> for (i=1;i<4;i++) {
> sum+=$i
> if (i==2)
> break
> }
> print sum
> }' num2
346
666
145
[root@server ~]# cat num2
123 223 456
122 544 23
112 33 45
备注:上述是当文件中执行到每行的第二列字段时跳出循环,重新执行下一行。
因为if是在'sum+=$i'后,所以计算出来的数据是前两列字段的和