shell编程之awk(数据筛选与处理)
Posted 锦衣admin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了shell编程之awk(数据筛选与处理)相关的知识,希望对你有一定的参考价值。
awk 命令介绍
常见的数据分析:
-
数据数据收集:负责数据的收集
-
数据清洗:负责数据的筛选
-
数据分析:数据运算、整理
-
数据展示:图表或表格方式输出结果
shell脚本数据的处理 :
- 数据检索:grep、tr、cut
- 数据处理:uniq、sort、tee、paste、xargs
之前的脚本都是通过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 语法
语法格式:
# 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":处理完数据流后执行,可选项
awk 的基本应用
awk 对字段(列)的提取
字段提取: 提取一个文本中的一列数据并打印输出
用逗号,
分隔字段内置变量打印
字段相关内置变量:
$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
awk 对记录(行)的提取
记录提取: 提取一个文本的一行并打印输出
用逗号,
分隔行号打印;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 程序的优先级
关于awk程序执行的优先级,BEGIN
是优先级最高的代码块,是在执行program之前执行,不需要提供数据源,因为不涉及到任何数据的处理,也不依赖于program代码块
program
是对数据流操作,是必选代码块,也是默认代码块。所以在执行时必须提供数据源
END
是处理完数据流后的操作,如果需要执行END代码块,就必须需要program的支持,单个无法执行
BEGIN 说明:
#l 不需要数据源就可以执行:
[root@server ~]# awk 'BEGIN{print "hello"}'
hello
程序优先级说明:
- program需要数据源,END需要program处理后的数据源
[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 高级应用
awk 是一本语言,那么就会符合语言的特性,除了可以定义变量外、还可以定义数组、还可以进行计算、还可以流程控制
-F命令选项
- 重新指定默认分隔符空格,并将
-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
awk 定义变量(-v命令)
在程序里面定义变量需要用冒号;
分割
#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
awk 定义数组
数组定义方式:数组名[索引]=值
- 定义数组时用冒号
;
分割
#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
awk 运算
- 注意在程序{program}内是无法进行运算的
awk 比较预算:>、>=、==、<、<=、!=
文件内容:
[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 比较运算:(不需要数据源的比较方式):
- 也可以做真假判断
- 根据返回内容。0代表假,1代表真
#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 备注:一个等于(=)会报错,大于(>)覆盖输入
awk 数学运算:+、-、*、/、**、%、++、+=、–、-=
- 默认支持浮点型。小数点精确到后四位
[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
awk 字符串匹配运算:==、!=、~、!~
- 还可以加入正则进行匹配
#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
awk 逻辑运算:&&、||
#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
awk 环境变量
变量 | 描述 |
---|---|
FIELDWIDTHS | 以空格分隔的数字列表,用空格定义每个数据字段的精确宽度 |
FS | 输入字段分隔符号,与-F命令类似(数据源的字段分隔符) |
OFS | 输出字段分隔符号,打印时不能打印$0 |
RS | 输入记录分隔符号,默认是回车 |
ORS | 输出记录分隔符号 ,默认是回车,打印时不能打印$0 |
FIELDWIDTHS:以空格重新分隔字段
-
重新定义列宽并打印,注意不可以使用
$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 shell 文本处理之 awk