AWK

awk的概述:
awk是一种编程语言,软件级别等同于bash,主要用于在linux/unix下对文本和数据进行处理。数据可以来自标准输入、一个或多个文件,或其它命令的输出。它支持用户自定义函数和动态正则表达式等先进功能,是linux/unix下的一个强大编程工具。它在命令行中使用,但更多是作为脚本来使用。awk的处理文本和数据的方式是这样的,它逐行扫描文件,默认从第一行到最后一行,寻找匹配的特定模式的行,并在这些行上进行你想要的操作。如果没有指定处理动作,则把匹配的行显示到标准输出(屏幕),如果没有指定模式,则所有被操作所指定的行都被处理。awk分别代表其作者姓氏的第一个字母。因为它的作者是三个人,分别是Alfred Aho、Brian Kernighan、Peter Weinberger。gawk是awk的GNU版本,它提供了Bell实验室和GNU的一些扩展。下面介绍的awk是以GNU的gawk为例的,在linux系统中已把awk链接到gawk,所以下面全部以awk进行介绍。
 

AWK



awk命令两种使用方式:
1)命令模式
awk [options] 'commands' file(s)
command 部分:/范围说明/{awk命令语句1;awk命令语句2;}
范围说明部分可以是BEGIN、END、逻辑表达式或者为空
awk命令语句间用分号间隔
引用shell 变量需用双引号引起
option 部分
-F 定义字段分割符号

2)脚本模式
awk [options] -f scriptfile file(s)
特点:
awk脚本是awk命令的清单
命令需要用分号间隔
#号开头的是注释行
# /bin/awk -f

字段及分割
awk 用$1,$2,$3...$n等的顺序形式表示files中每行以间隔符号分割的各列的不同字段
$0表示行本身
awk默认以空格符为间隔符号将每行分割为单独的字段,也可以使用awk内置变量FS定义间隔符号
awk 使用option中的-F参数定义默认间隔符号
NF变量表示当前记录的字段数(列数)
$NF 最后一列
$(NF-1) 倒数第二列
FNR/NR 行号
FILENAME 文件名
"\t" 制表符
RS 换行符
"" 打印字符串

# awk 'BEGIN {FS=":"} {print $1}' /etc/passwd
$ head -5 /etc/passwd > passwd
$ awk -F':' '{print FILENAME,FNR,NF,$1,$2,$3,$4,$5,$6,$7,"\t"$0}' ./passwd
./passwd 1 7 root x 0 0 root /root /bin/bash root:x:0:0:root:/root:/bin/bash
./passwd 2 7 bin x 1 1 bin /bin /sbin/nologin bin:x:1:1:bin:/bin:/sbin/nologin
./passwd 3 7 daemon x 2 2 daemon /sbin /sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin
./passwd 4 7 adm x 3 4 adm /var/adm /sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin
./passwd 5 7 lp x 4 7 lp /var/spool/lpd /sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin

打印所在行
$ cat passwd
$ awk -F':' '{print $0}' passwd
$ awk -F':' '{print}' passwd

取倒数第一列和倒数第二列
$ head -1 passwd |awk -F':' '{print $NF,$(NF-1)}'
/bin/bash /root

换行符(默认处理一行后才换行)
$ head -1 passwd |awk -F':' '{print $NF RS $(NF-1)}'
/bin/bash
/root
$head -1 passwd | awk 'BEGIN {FS=":"} {print $NF RS $(NF-1)}'

一次指定多个分隔符
$ awk -F':|/' '{print NF}' passwd
$ awk -F'[:/]' '{print NF}' passwd
$ ifconfig eth0|grep Bcast|awk -F':| +' '{print $4}'
$ifconfig eth0 |grep Bcast | awk -F" " '{print $2}'|awk -F":" '{print $2}'

定址:
1)关键字
BEGIN :表示在程序开始前执行
END :表示所有文件处理完后执行
操作流程:
#/bin/awk -f
BEGIN {} --读前处理(读取目标文件)
{} --行处理
END {} --读后处理

打印标题和结尾信息
$ awk -F':' 'BEGIN {print "user\tpasswd\thome"RS"----------------------"} ; {print $1"\t"$2"\t"$(NF-1)} ; END {print "<-----------END---------->"}' passwd
user passwd home
----------------------
root x /root
bin x /bin
daemon x /sbin
adm x /var/adm
lp x /var/spool/lpd
<-----------END---------->

2)正则表达式
$ awk '/root/ {print $0}' /etc/passwd --使用普通字符定位
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

$ awk '$0 ~ /^root/ {print $0}' /etc/passwd --使用正则表达式定位
root:x:0:0:root:/root:/bin/bash


$ awk '/^[rR]oot|^[bB]in/ {print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
# awk -F':' '/^[rR]oot|^[bB]in/ {print $1}' passwd

使用正则表达式精确定位
$ awk -F':' '$2=="" {print $0}' /etc/passwd
$ awk -F':' '$2=="" {print $0}' /etc/shadow --查看用户中有无空密码的
$ awk -F':' '$7 ~ /bash$/ {print $0}' /etc/passwd
$awk -F: '$NF ~ /bash$/ {print $1}' /etc/passwd
$ awk -F':' 'NR>=5 && NR<=10 {print $0}' /etc/passwd
$ awk '/^root/,/^uucp/ {print $0}' /etc/passwd

3)逻辑表达式
例如:NR>40
例如:if ( $2>50) { print $3 }
逻辑表达式:
==(等于)、!=(不等于)、>(大于)、<(小于)、>=(大于等于)、<=(小于等 于)
~(匹配于)和!~(不匹配于)
!(非)、&&(与)、||(或)、和括号()

awk -F: 'NR<10 && NR>3 {print $0} ' /etc/passwd --与
awk -F: 'NR==10 || NR==3 {print NR,$0} ' /etc/passwd --或
awk -F: '!(NR<40) {print NR,$1}' /etc/passwd --取反
awk -F: '$3>=500 && $3 <=60000 {print $3,$1}' /etc/passwd --打印普通用户
awk -F: 'NR%2==0 {print NR,$0}' /etc/passwd -- 取偶数
awk -F: 'NR%2!=0 {print NR,$0} ' /etc/passwd --打印奇数行
awk -F: 'NR%2==1 {print NR,$0}' /etc/passwd
awk -F":" 'BEGIN {print "user_UID\tusername\t"RS"-------------------"}; ($3>=500 && $3<=60000) {print $3"\t","\t"$1} ; END {print "--------------------"}' /etc/passwd

awk的流程控制
if
for
while

if(expr1) action
if(expr1) action2;else aciton2
if(expr1) action1;else if(expr2);else

# awk -F: '{if($3>500) print $1,$3,"普通用户"}' /etc/passwd
# awk -F: '{if($3>500) print $1,$3,"普通用户";else print $1,$3,"不是普通用户"}' /etc/passwd
# awk -F: '{if($3==0) print $1,$3,"管理员";else if($3<500) print $1,$3,"系统用户";else print $1,$3,"普通用户"}' /etc/passwd

for
# awk 'BEGIN {for(i=0; i<=10; i++) print i}'

while
# awk 'BEGIN {i=0;while(i<=5) {print i;i++}}'
# awk 'BEGIN {i=0;while(i<=5) {print"service",i,"start";i++}}'

#循环的控制:
break --条件满足的时候中断循环
continue --条件满足的时候跳过循环
# awk 'BEGIN {i=0;while(i<=5) {i++;if(i==3) continue; print i}}'
# awk 'BEGIN {i=0;while(i<=5) {i++;if(i==3) break; print i}}


用netstat -ntl 截取所有开放的端口号
netstat -ntl |grep -v Active| grep -v Proto|awk '{print $4}'|awk -F: '{print $NF}'
netstat -ntlup |grep -Ev "Active|Proto" |awk '{print $4}' |awk -F: '{print $NF}'

截取权限
[[email protected] ~]# stat aa| grep Uid | cut -c10-13
0644
[[email protected] ~]# stat aa| grep Uid | awk -F':' '{print $2}' | awk -F'[(/]' '{print $2}'

 

示例:


例1:监控磁盘使用率,高于80%的找出来,输出警告,发送邮件给管理员

# df -h |grep -v Filesystem |awk '{print $1,$5}'|awk -F% '{print $1}'| awk '$2>80 {print $1"useage is over 80%,warning!!!"}' |mail -s "disk overuse problem" root

# df -h | awk 'NR>1 {print $4"\t"$5}' | grep % | grep -v G | grep -v M | awk -F'%' '$1>80 {print $1"useage is over 80%,warning!!!"}' | mail -s "warring" [email protected]

# df -k |grep -v Filesystem |awk '$3/$2>0.8 {print $1"useage is over 80%,warning!!!"}' |mail -s "disk overuse problem" root

例2:监控CPU 15分钟内的平均负载,如果超过5则发警告邮件给管理员
uptime |awk '$NF>5 {print "high load,warning"}' |mail -s 'xxxxx' root

例3:计算swap总量,使用量,剩余量,使用百分比,剩余百分比

格式要求为:
total used free used% free%
xxxxx xxxx xxxx xxx% xxx%

free |tail -1 |awk 'BEGIN {print "total\t\tused\tfree\tused%\tfree%"} {print $2"\t\t"$3"\t"$4"\t"$3/$2*100"%\t"$4/$2*100"%"}'
total used free used% free%
4096564 0 4096564 0% 100%

利用printf格式化输出

# awk 'BEGIN {print "hello world"}'
hello world
# awk 'BEGIN {printf "hello world"}'
hello world#
--从上面看到print和printf直接使用的主要区别就是printf不自动换行,需要使用\n来换行
# awk 'BEGIN {printf "Hello world\n" }'
Hello world

格式:
printf {format_expression[,argument]}
两个主要的格式说明符是s和d,s代表字符串,d表示十进制整数

# ls -l | grep -v total | awk '{printf ("%s的大小为%d\n",$NF,$5)}' |tail
Templates的大小为4096
test的大小为25
test1的大小为9
test.sh的大小为258
test*?[].sh的大小为0
user_passwd.txt的大小为60
Videos的大小为4096
while_useradd_shift.sh的大小为100
who.out的大小为320
内部变量的大小为76

awk -F: 'BEGIN {sum=0} $NF=="/sbin/nologin" {printf ("%s\t",$1); sum=sum+1} END {print "\n\n总共有:"sum"个"}' /etc/passwd
bin daemon adm lp mail uucp operator games gopher ftp nobody vcsa rpc mailnull smmsp nscd ntp oprofile pcap dbus avahi xfs named rpcusernfsnobody sshd haldaemon gdm avahi-autoipd sabayonapache squid ais mfs postfix distcache cacti

总共有:37个

1,利用awk把不允许登录的账号名打印出来(指第七列不是以/bin/bash结束的列),并统计个数
# awk -F: 'BEGIN {sum=0} $NF!~"/bin/bash" {printf ("%s ",$1);sum=sum+1} END {print "\n\n总共的个数 为:"sum}' /etc/passwd

2,把系统上ssh的登录日志格式化打印成类似下面的

*********************************************************
time hostname user ip port protocol
Jul-15 15:47:20 li root 2.2.2.35 6703 ssh2
*********************************************************
*********************************************************
time hostname user ip port protocol
Jul-15 15:47:27 li root 2.2.2.35 6704 ssh2
*********************************************************

# cat /var/log/secure |grep Accepted |awk '{print "***************************************************************************"}{print "time\t\thostname\tuser\tip\t\tport\tprotocol"}{print $1"-"$2,$3"\t"$4"\t\t"$9"\t"$11"\t"$13"\t"$14}{print "*************************************************************************"}'

# cat /var/log/secure |grep Accepted |awk ' BEGIN{print "time\t\thostname\tuser\tip\t\tport\tprotocol"}{print $1"-"$2,$3"\t"$4"\t\t"$9"\t"$11"\t"$13"\t"$14}'


vim awk.sh示例:

#!/bin/awk -f
#/etc/shadow
#**************************************
#用户名 用户创建时间 密码过期时间
#++++++++++++++++++++++++++++++++++++++
#
#
#***************************************
{FS=":"} BEGIN {print "************************" RS "用户名 ""\t""用户创建时>间""\t""密码过期时间""\t" RS "++++++++++++++++++++++++++++++++++"};
{print $1"\t\t"$3"\t"$5"\t"};
END {print "************************************"}

[[email protected] tmp]# ./awk.sh /etc/shadow