Shell Script awk命令
一、awk
一)awk介绍
awk是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大。简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理。
awk有3个不同版本: awk、nawk和gawk,未作特别说明,一般指gawk
awk语言的最基本功能是在文件或字符串中基于指定规则来分解抽取信息,也可以基于指定的规则来输出数据
二)awk三种调用方式
命令执行方式:awk [opion] ‘awk_script’ input_file1 [input_file2 …]
将awk_script放入脚本文件并以#!/bin/awk -f 作为首行,给予该脚本可执行权限,然后在shell下通过键入该脚本的脚本名调用之
将所有的awk_script插入一个单独脚本文件,然后调用: awk -f awk脚本文件input_file(s)
三)awk_opion
awk [opion] ‘awk_script’ input_file1 [input_file2 …]
awk的常用选项option有:
-F fs : 使用fs作为输入记录的字段分隔符,如果省略该选项,awk使用环境变量IFS的值
-f filename : 从文件filename中读取awk_script
-v var=value : 为awk_script设置变量
四)awk_script
awk [opion] 'awk_script’input_file1 [input_file2 …]
awk_script可以由一条或多条awk_cmd组成,可以有多个awk_cmd
awk_cmd由两部分组成: awk_pattern { actions }
awk_script可以被分成多行书写,必须确保整个awk_script被单引号括起来
五)awk入门简单案例
1.案例1
显示当前操作系统最近登录的5个IP地址和用户
[root@localhost ~]# last -n 5
root pts/0 192.168.100.1 Mon Oct 14 07:22 still logged in
root tty1 Mon Oct 14 07:21 still logged in
reboot system boot 3.10.0-1160.118. Mon Oct 14 07:19 - 07:22 (00:02)
root pts/0 192.168.100.1 Sat Oct 5 01:51 - crash (9+05:28)
root tty1 Sat Oct 5 01:35 - crash (9+05:44) [root@localhost ~]# last -n 5 | awk '{print $1","$3}'
root,192.168.100.1
root,Mon
reboot,boot
root,192.168.100.1
root,Sat
awk工作流程是这样的:读入有’\n’换行符分割的一条记录,然后将记录按指定的域分隔符划分域,填充域,$0则表示所有域, 1 表示第一个域 , 1表示第一个域, 1表示第一个域,n表示第n个域。默认域分隔符是"空白键" 或"[tab]键",所以$1表示登录用户,$3表示登录用户ip,以此类推。
2.案例2
显示/etc/passwd的账户和账户对应的shell,而账户与shell之间以tab键分割。
[root@localhost ~]# awk -F: '{print $1,$7}' /etc/passwd
root /bin/bash
bin /sbin/nologin
daemon /sbin/nologin
adm /sbin/nologin
lp /sbin/nologin
sync /bin/sync
shutdown /sbin/shutdown
halt /sbin/halt
mail /sbin/nologin
……
在上面的基础上添加列名name 和shell,在最后一行添加“end"。
[root@localhost ~]# awk -F: 'BEGIN {print "name\tshell"} {print $1","$7} END {print "end"}' /etc/passwd
name shell
root,/bin/bash
bin,/sbin/nologin
daemon,/sbin/nologin
adm,/sbin/nologin
lp,/sbin/nologin
sync,/bin/sync
shutdown,/sbin/shutdown
halt,/sbin/halt
mail,/sbin/nologin
……
end
六)awk命令的一般形式
awk -F ' BEGIN { actions }awk_pattern1 { actions ............awk_patternN { actions }END { actions }
' inputfile
其中BEGIN { actions } 和END { actions } 是可选的
七)awk的运行过程
1.如果BEGIN 区块存在,awk执行它指定的actions
2.awk从输入文件中读取一行,称为一条输入记录(如果输入文件省略,将从标准输入读取)
3.awk将读入的记录分割成字段,将第1个字段放入变量$1中,第2个字段放入$2,以此类推。$0表示整条记录。字段分隔符使用shell环境变量IFS或由参数指定
4.把当前输入记录依次与每一个awk_cmd中awk_pattern比较,看是否匹配,如果相匹配,就执行对应的actions。如果不匹配,就跳过对应的actions,直到比较完所有的awk_cmd
5.当一条输入记录匹配所有的awk_cmd后,awk读取输入的下一行,继续重复步骤3和4,这个过程一直持续,直到awk读取到文件尾
6.当awk读完所有的输入行后,如果存在END,就执行相应的actions。
八)awk的语法规则总结
iput_file可以是多于一个文件的文件列表,awk将按顺序处理列表中的每个文件
一条awk_cmd的awk_pattern可以省略,省略时不对输入记录进行匹配比较就执行相应的actions
一条awk_cmd的actions 也可以省略,省略时默认的动作为打印当前输入记录(print $0)
一条awk_cmd中的awk_pattern和actions不能同时省略
BEGIN区块和END区块分别位于awk_script的开头和结尾
awk_script中只有END区块或者只有BEGIN区块是被允许的
如果awk_script中只有BEGIN { actions } ,awk不会读取input_file
awk把输入文件的数据读入内存,然后操作内存中的输入数据副本,awk不会修改输入文件的内容
awk的总是输出到标准输出,如果想让awk输出到文件,可以使用重定向
二、awk高级部分
一)awk_pattern的几种类型
awk_pattern { actions }
awk_pattern模式部分决定actions动作部分何时触发及触发actions
正则表达式用作awk_pattern
布尔表达式用作awk_pattern
逻辑操作符组合awk_pattern
逗号分隔的两个正则表达式,表示选择范围
正则表达式用作awk_pattern: /regexp/
awk中正则表达式匹配操作中经常用到的字符
^ $ . [] | () * // 通用的regexp元字符
+ : 匹配其前的单个字符一次以上,是awk自有的元字符,不适用于grep或sed等
? : 匹配其前的单个字符1次或0次,是awk自有的元字符,不适用于grep或sed等
查找所有zabbix用户的进程和进程PID
[root@localhost ~]# ps -ef | awk '/^zabbix/ {print $1,$2}'
zabbix 1024
zabbix 1028
zabbix 1029
zabbix 1030
zabbix 1031
zabbix 1032
布尔表达式用作awk_pattern,表达式成立时,触发相应的actions执行
表达式中可以使用变量(如字段变量$1,$2等)和/regexp/
布尔表达式中的操作符
关系操作符: < > <= >= == !=
匹配操作符:
value ~ /regexp/ 如果value匹配/regexp/,则返回真
value !~ /regexp/ 如果value不匹配/regexp/,则返回真。
awk’$2 > 10 {print “ok”}’ input_file
awk’$3 ~ /^d/ {print “ok”}’ input_file
[root@localhost ~]# ps -ef | awk '$2 > 1400 {print $1,$2}'
UID PID
root 1404
root 1437
root 1464
root 1471
root 1484
root 1485
逻辑操作符组合awk_pattern
&&(与) 和||(或) 可以连接两个/regexp/或者布尔表达式,构成混合表达式
!(非) 可以用于布尔表达式或者/regexp/之前
awk ‘($1 < 10 ) && ($2 > 10) {print “ok”}’ input_file
awk ‘/^d/ || /x$/ {print “ok”}’ input_file
[root@localhost ~]# ps -ef | awk '($2 > 1000) && ($2 < 1100) {print $1,$2}'
root 1017
root 1019
root 1020
zabbix 1024
zabbix 1028
zabbix 1029
zabbix 1030
zabbix 1031
zabbix 1032
二)awk高级特性
1.awk支持与特性
awk支持内置变量:
ARGC 命令行参数个数
ARGV 命令行参数排列
ENVIRON 支持队列中系统环境变量的使用
FILENAME awk浏览的文件名
FNR 浏览文件的记录数
FS 设置输入域分隔符,等价于命令行F选项
NF 浏览记录的域的个数
NR 已读的记录数
OFS 输出域分隔符
ORS 输出记录分隔符
RS 控制记录分隔符
# NR的使用
[root@localhost ~]# awk -F: '{print NR,$1, $7}' /etc/passwd
1 root /bin/bash
2 bin /sbin/nologin
3 daemon /sbin/nologin
4 adm /sbin/nologin
5 lp /sbin/nologin
6 sync /bin/sync
7 shutdown /sbin/shutdown
8 halt /sbin/halt
9 mail /sbin/nologin
……
支持函数
print、split、substr、sub、gsub
# split切割字符串
[root@localhost ~]# echo "today is 2024-01-01" | awk '{split($3,my_arr,"-");print my_arr[2]}'
01
支持流程控制语句,类C语言
if、while、do/while、for、break、continue
支持自定义变量,数组
a[1] a[tom] map(key)
2.awk高级案例
搜索/etc/passwd有root关键字的所有行
[root@localhost ~]# awk '/root/ {print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
统计/etc/passwd文件中账户人数
[root@localhost ~]# awk '{count++} END {print "用户数量:", count}' /etc/passwd
用户数量: 26
使用for循环显示/etc/passwd的所有账户。显示格式:序号 账户名
# 直接输出
[root@localhost ~]# awk -F: 'BEGIN {i=0;} {users[i]=$1;i++} END {for(n in users){print n,users[n]}}' /etc/passwd
17 sshd
4 lp
18 postfix
5 sync
19 chrony
6 shutdown
7 halt
8 mail
9 operator
10 games
20 apache
……# 按顺序输出
[root@localhost ~]# awk -F: 'BEGIN {count=0;} {users[count]=$1;count++} END {for(i = 0;i<count;i++){print i,users[i]}}' /etc/passwd
0 root
1 bin
2 daemon
3 adm
4 lp
5 sync
6 shutdown
7 halt
8 mail
……
三、awk综合案例
统计某个文件夹下的文件占用的字节数,过滤4096大小的文件(一般都是文件夹)
[root@localhost ~]# ls -l /etc/ | awk 'BEGIN {sum=0;print "统计/etc下的文件总大小";} {if($5 !=4096){sum+=$5}} END {print "总大小为:",sum/1024/1024,"M"}'
统计/etc下的文件总大小
总大小为: 0.903872 M
统计报表:
合计每人1月工资
员工角色码值:0:manager,1:worker
Tom 0 2012-12-11 car 3000
John1 2013-01-13 bike 1000
vivi1 2013-01-18 car 2800
Tom0 2013-01-20 car 2500
John1 2013-01-28 bike 3500
[root@localhost ~]# cat user.txt
Tom 0 2012-12-11 car 3000
John 1 2013-01-13 bike 1000
vivi 1 2013-01-18 car 2800
Tom 0 2013-01-20 car 2500
John 1 2013-02-28 bike 3500# 编写awk脚本
[root@localhost ~]# vi awk.my
#!/bin/awk -f
BEGIN {print "姓名\t工资\t角色";i=0;
}
{split($3,date,"-");if(date[2]=="01"){name[i]=$1;sal[i]=$5;if($2==0){role[i]="经理";}else{role[i]="普通员工";}}i++;
}
END {for(n=0;n<i;n++){print name[n]"\t"sal[n]"\t"role[n];}
}# 添加可执行权限
[root@localhost ~]# chmod 755 awk.my# 执行脚本
[root@localhost ~]# ./awk.my user.txt
姓名 工资 角色
John 1000 普通员工
vivi 2800 普通员工
Tom 2500 经理