简介
awk是数据处理工具也是报告生成工具。相比于sed常常作用于一整行的处理,awk则比较倾向于将一行分为数个字段来处理。因此,awk相当适合处理小型的数据处理。默认情况下awk将下面的变量分配给在文本行中检测到的每个数据字段:$0:表示整行文本;$1:表示文本行中的第一个数据字段;$2:表示文本行中的第二个数据字段;$n:表示文本行中的第n个数据字段。
awk用法格式是:awk [options]‘模式类型1{动作1}’FILE1...
常用选项
选项 |
描述 |
-F FS |
指定描述一行中的数据字段的文件分隔符 |
-f awk_script |
指定读取程序的文件名,可指定载入脚本 |
-v VAR_VALUE |
定义awk程序中使用的变量和默认值 |
模式类型
空模式:没有指定任何模式,遍历文件中的每一行
1
2
3
4
5
6
|
[root@zhao ~]
# awk -F: '{print $1,$7}' /etc/passwd
root
/bin/bash
bin
/sbin/nologin
daemon
/sbin/nologin
adm
/sbin/nologin
lp
/sbin/nologin
|
BEGIN模式:在执行所有awk脚本动作之前执行的预设类(准备)的工作
1
2
3
4
5
6
7
|
[root@zhao ~]
# awk -F: 'BEGIN {print "USERNAME SHELL"}{print $1,$7}' /etc/passwd
USERNAME SHELL
#使用BEGIN模式设定的预设字符
root
/bin/bash
bin
/sbin/nologin
daemon
/sbin/nologin
adm
/sbin/nologin
lp
/sbin/nologin
|
小拓展:分隔符包含字段分隔符(默认为空白符)和行分隔符(默认为换行符)其中使用FS:指定输入分隔符也是awk的内置变量;OFS指定输出分隔符;NF指定最后一个字段。
FS:指定输入分隔符
1
2
3
4
5
6
|
[root@zhao ~]
# awk 'BEGIN {FS=":"}{print $1,$7}' /etc/passwd
root
/bin/bash
bin
/sbin/nologin
daemon
/sbin/nologin
adm
/sbin/nologin
lp
/sbin/nologin
|
OFS:指定输出分隔符
1
2
3
4
5
6
|
[root@zhao ~]
# awk 'BEGIN {FS=":";OFS="*********"}{print $1,$7}' /etc/passwd
root*********
/bin/bash
bin*********
/sbin/nologin
daemon*********
/sbin/nologin
adm*********
/sbin/nologin
lp*********
/sbin/nologin
|
NF:指定最后一个字段
1
2
3
4
5
6
|
[root@zhao ~]
# df -lh | awk '!/^File/{printf"%-25s %s\n",$1,$NF}'
/dev/mapper/vg0-root
/
tmpfs
/dev/shm
/dev/sda1
/boot
/dev/mapper/vg0-usr
/usr
/dev/mapper/vg0-var
/var
|
END模式:在awk脚本动作执行完成之后做的简单的收尾操作,主要用于统计使用
1
2
3
4
5
6
7
8
|
[root@zhao ~]
# awk 'BEGIN {FS=":";OFS="***";print "USERNAME SHELL"}{print $1,$7}END {print "********END******"}' /etc/passwd
USERNAME SHELL
root***
/bin/bash
bin***
/sbin/nologin
daemon***
/sbin/nologin
sshd***
/sbin/nologin
tcpdump***
/sbin/nologin
********END******
|
正则表达式:(格式:/PATTERN/)表示对于被正则表达式匹配到的行进行操作,而并非所有行
1
2
3
4
5
6
7
|
[root@zhao ~]
# awk -F: '/in$/{print $1,$7}' /etc/passwd
bin
/sbin/nologin
daemon
/sbin/nologin
adm
/sbin/nologin
lp
/sbin/nologin
mail
/sbin/nologin
uucp
/sbin/nologin
|
表达式:其值非0或为非空字符时满足条件,如:$1 ~ /foo/ 或 $1 == "magedu"
支持使用~(匹配)和!~(不匹配),>=,<=,>,< 等所有比较操作符
1
2
3
4
5
6
7
|
[root@zhao ~]
# awk -F: '$3>=50{print $1,$3}' /etc/passwd
nobody 99
dbus 81
usbmuxd 113
vcsa 69
rtkit 499
avahi-autoipd 170
|
支持逻辑关系操作符
1
2
3
|
[root@zhao ~]
# awk -F: '$3>=500 && $7~/bash/{printf "%-15s %i %s\n",$1,$3,$7}' /etc/passwd
user1 500
/bin/bash
user2 501
/bin/bash
|
指定匹配范围,格式为pat1,pat2
这个匹配模式类型很难理解,我们用一个例题来解析:
eg:匹配一个文件中以abc开头aaa结尾的字段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
[root@zhao ~]
# awk '/abc/,/aaa/{print $0}' awk.sh
abc 3
#开始
cde 4
aaa 5
#结束
abc 6
#开始
aab 7
axd 8
hell0 9
aaa 10
#结束
abc how are you aaa
#开始结束
abc 12
#开始
sss
ddd
sxd
xvf
#无结尾字段会直接遍历到文件尾部
|
输出动作介绍
1、print
print的使用格式:print item1, item2, ...
要点:
(1)、各项目之间使用逗号隔开,而输出时则以空白字符分隔;
(2)、输出的item可以为字符串或数值、当前记录的字段(如$1)、变量或awk的表达式;数值会先转换为字符串,而后再输出;
(3)、print命令后面的item可以省略,此时其功能相当于print $0, 因此,如果想输出空白行,则需要使用print"";
2、printf
printf命令的使用格式:printf format, item1, item2,...
要点:
(1)、其与print命令的最大不同是,printf需要指定format;
(2)、format用于指定后面的每个item的输出格式;
(3)、printf语句不会自动打印换行符;\n
其中format格式的指示符都以%开头,后跟一个字符;如下:
%c: 显示字符的ASCII码;
%d, %i:十进制整数;
%e, %E:科学计数法显示数值;
%f: 显示浮点数;
%g, %G: 以科学计数法的格式或浮点数的格式显示数值;
%s: 显示字符串;
%u: 无符号整数;
%%: 显示%自身;
在输出格式上常用修饰符:
N: 显示宽度;
-: 左对齐(默认为右对齐);
+:显示数值符号;
eg:
1
2
3
4
5
6
7
|
[root@zhao ~]
# awk -F: '{printf "%-15s %i\n",$1,$3}' /etc/passwd
root 0
bin 1
daemon 2
adm 3
lp 4
sync
5
|
控制语句的使用
常见类型:if;while; for; case; do...while; break; continue; next
(1)、if-else
语法:if (condition){then-body} else {[else-body]}
解析:condition是判断条件通常为布尔表达式或逻辑表达式
1
2
3
4
5
6
7
8
|
[root@zhao ~]
# awk -F: '{if ($3==0) {print $1, "Adminitrator";} else { print $1,"Common User"}}' /etc/passwd
root Adminitrator
bin Common User
daemon Common User
adm Common User
lp Common User
sync
Common User
shutdown
Common User
|
1
2
|
[root@zhao ~]# awk -F: -v sum=
0
'{if ($3>=500) sum++}END{print sum}'
/etc/passwd
3
#所有UID大于
500
的用户个数
|
(2)、while
首先说明在awk中的循环不是循环每一行的而是循环切片之后的每一个字段的
语法: while(condition){statement1; statment2; ...}
1
2
3
4
5
6
7
|
[root@zhao ~]
# awk -F: '{i=1;while (i<=3) {print $i;i++}}' /etc/passwd
root
#i=1时输出第一个字段
x
#i=2时输出第二个字段
0
#i=3时输出第三个字段
bin
#第二行...
x
1
|
1
2
3
4
5
6
|
[root@zhao ~]
# awk -F: '{i=1;while (i<=NF) { if (length($i)>=4) {printf "%s ", $i}; i++ ;};printf "\n"}' /etc/passwd
root root
/root
/bin/bash
/bin
/sbin/nologin
daemon daemon
/sbin
/sbin/nologin
/var/adm
/sbin/nologin
/var/spool/lpd
/sbin/nologin
|
解析:length是awk的内置函数,获取字段的长度的;而这里length($i)指的是$i字段的长度。
(3)、do-while
和while循环的区别:while循环是当条件不满足时不会执行任何循环操作,而do-while是不论条件真假与否首先执行一次循环体,然后再去判断条件满足与否,满足继续执行不满足不再执行。也就是说不管条件满足与否,至少执行一次循环体。
语法: do{statement1, statement2, ...} while (condition)
1
2
3
4
5
6
|
[root@zhao ~]
# awk -F: '{i=4;do {print $i;i--}while(i>4)}' /etc/passwd
0
1
2
4
7
|
解析:此命令中的while循环并不成立但是还是执行了print$4这就是do-while的体系结构若还不太明白可对比使用:[root@zhao ~]# awk -F: '{i=4;while (i>4){print $i;i--}}'/etc/passwd此命令执行之后不会有任何执行结果。
(4)、for
语法: for ( 初始赋值; condition(变量条件);修正变量(初始值)) { statement1, statement2, ...}
1
2
3
4
5
6
7
8
|
[root@zhao ~]
# awk -F: '{for(i=1;i<=3;i++) print $i}' /etc/passwd
root
x
0
bin
x
1
daemon
|
(5)、case
语法:switch(expression) { case VALUE or /REGEXP/: statement1, statement2,... default:statement1, ...}
由于此语句不常使用所以不做深入研究,有意学习者请参考其他文章
(6)、break 和 continue
常用于循环或case语句中,具体使用方法和bash中基本类似,所以此处不做重点阐述
(7)、next
这个为awk的独有命令,表示提前结束对本行文本的处理,并接着处理下一行;
例如,显示其ID号为奇数的用户:
1
2
3
4
5
6
7
8
|
[root@zhao ~]
# awk -F: '{if($3%2==0) next;print $1,$3}' /etc/passwd
bin 1
adm 3
sync
5
halt 7
operator 11
gopher 13
nobody 99
|
(8)、数组应用
数组:一组连续的可存储多个值内存空间,可使用数组索引(下标)来引用数组。
语法:array[index-expression]
index-expression可以使用任意字符串;需要注意的是,如果某数据组元素事先不存在,那么在引用其时,awk会自动创建此元素并初始化为空串;因此,要判断某数据组中是否存在某元素,需要使用index in array的方式。
若遍历数组中的每一个元素,需要使用如下的特殊结构:
语法:for (var in array) {statement1, ... }
其中,var用于引用数组下标,而不是元素值;
1
2
3
4
5
6
|
[root@zhao ~]
# awk -F: '$NF!~/^$/{BASH[$NF]++}END{for(A in BASH){printf "%15s:%i\n",A,BASH[A]}}' /etc/passwd
/sbin/shutdown
:1
/bin/bash
:3
/sbin/nologin
:31
/sbin/halt
:1
/bin/sync
:1
|
解析:$NF为最后一个字段,^$表示为空,BASH[$NF]表示下标为$NF数组,也就是说可能出现BASH[/bin/bash]等
awk的内置函数使用与介绍
-
split(string, array[, fieldsep [, seps ] ])
解析:将string表示的字符串以fieldsep为分隔符进行分隔,并将分隔后的结果保存至array为名的数组中;数组下标为从1开始的序列;
1
2
3
4
|
[root@zhao ~]
# netstat -tan | awk '/:80\>/{split($5,clients,":");ip[clients[4]]++}END{for(a in ip) print ip[a],a}' | sort -rn | head -50
100 172.16.18.11
1 *
##########此命令在不开启IPv6的centos6.4上执行的###########
|
例题解析:split($5,clients,":")是使用冒号分割将第5段保存到clients数组中;IP[clients[1]]++}提取第一部分(IP地址)保存到IP这个变量中;{for(iin IP){print IP[i],i}}遍历IP地址并显示次数和IP地址;sort -rn 根据次数进行逆向排序,head -5O只取前50个。
例:取出当前使用比例已经大于20%的文件系统
1
2
|
[root@zhao ~]
# df -lh | awk '!/^File/{split($5,percent,"%");if(percent[1]>=20){print $1}}'
/dev/mapper/vg0-usr
|
-
length([string])
解析:返回string字符串中字符的个数;
-
substr(string, start[, length])
解析:取string字符串中的子串,从start开始,取length个;start从1开始计数;
-
system(command)
解析:执行系统command并将结果返回至awk命令
-
systime()
解析:取系统当前时间
-
tolower(s)
解析:将s中的所有字母转为小写
-
toupper(s)
解析:将s中的所有字母转为大写
至此awk的应用知识基本阐述完毕,如有错误或无阐述到位的问题请留言注明,大家的支持是我学习路上前进的动力!!
本文转自 z永 51CTO博客,原文链接:http://blog.51cto.com/pangge/1286728