awk是Linux以及UNIX环境中现有的功能最强大的数据处理工具,awk其名称得自于它的创始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首个字母
awk是一种处理文本数据的编程语言,适合文本处理和报表生成,awk的设计使得它非常适合于处理由行和列组成的文本数据。
awk 还是一种编程语言环境,它提供了正则表达式的匹配,流程控制,运算符,表达式,变量以及函数等一系列的程序设计语言所具备的特性,它从C语言中获取了一些优秀的思想
pattern
)和动作(action
)组成
[root@server ~]# vim input # 输入多个回车
[root@server ~]# awk '/^$/{print "This is a blank line."}' input
awk -f program-file file
-f选项表示从脚本文件中读取awk程序语句,program-file表示awk脚本文件名称,file表示要处理的数据文件
例
[root@server ~]# vim scr.awk # 输入以下内容
/^$/{print "This is a blank line."}
[root@server ~]# awk -f scr.awk input # 使用命令及脚本结合的方法执行
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
#!/bin/awk -f
./awk-script.awk file
[root@server ~]# vim awktest.awk
#!/bin/awk -f # 注意:awk脚本解释器
/^$/{print "This is a blank line."}
[root@server ~]# chmod +x awktest.awk # 赋予执行权限
[root@server ~]# ./awktest.awk input # 执行awk脚本后跟上被处理的文件名
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
This is a blank line.
awk 'BEGIN{ commands } pattern{ commands } END{ commands }' [INPUTFILE…]
# 以上三部分可选
BEGIN模式是一种特殊的内置模式,其执行的时机为awk程序刚开始执行,但是又尚未读取任何数据之前。因此,该模式所对应的操作仅仅被执行一次,当awk读取数据之后,BEGIN模式便不再成立。所以,用户可以将与数据文件无关,而且在整个程序的生命周期中,只需执行1次的代码放在BEGIN模式对应的操作中,一般用于打印报告的标题和更改内在变量的值
END模式是awk的另外一种特殊模式,该模式执行的时机与BEGIN模式恰好相反,它是在awk命令处理完所有的数据,即将退出程序时成立,在此之前,END模式并不成立。无论数据文件中包含多少行数据,在整个程序的生命周期中,该模式所对应的操作只被执行1次。因此,一般情况下,用户可以将许多善后工作放在END模式对应的操作中EN,一般用于打印总结性的描述或数值总和
例
[root@server ~]# awk 'BEGIN{print "BEGIN...."} {print $0} END{print "The End"}' /etc/fstab
awk 'BEGIN{ commands } {print item1,item2,……} END{ commands }' [INPUTFILE…]
$
来指定执行动作的域,域操作符$
后面跟数字或变量来标识域的位置,每条记录的域从1开始编号,如$1
表示第一个域 $0
表示所有域# 准备示例文件
[root@server ~]# vim awk1.txt
Li xiaoming xian 13311111111
zhang cunhua bapji 133222222222
wang xiaoer xianyang 13333333333
[root@server ~]# awk '{print $0}' awk1.txt # 打印所有列(域)
Li xiaoming xian 13311111111
zhang cunhua bapji 133222222222
wang xiaoer xianyang 13333333333
[root@server ~]# awk '{print $1}' awk1.txt
Li
zhang
wang
[root@server ~]# awk '{print $2}' awk1.txt
xiaoming
cunhua
xiaoer
[root@server ~]# awk '{print $3}' awk1.txt
xian
bapji
xianyang
[root@server ~]# awk '{print $4}' awk1.txt
13311111111
133222222222
13333333333
[root@server ~]# awk 'BEGIN{one=1;two=2} {print $(one+two)}' awk1.txt
xian
bapji
xianyang
# 查看本机IP
[root@server ~]# ip a | grep ens160 | awk '/inet/{print $2}'
# 查看内存剩余容量
[root@server ~]# free -m | awk '/Mem/{print $4}'
# 查看开机挂载设备的文件系统
[root@server ~]# awk '/^[^#]/{print $3}' /etc/fstab
# 查看hosts文件的IP地址
[root@server ~]# awk '{print $1}' /etc/hosts
# 语法: awk -F "符号" '/模式/{动作}' 文件名
# 查看本机所有账户的名称及UID
[root@server ~]# awk -F ":" '{print $1,$3}' /etc/passwd
# 语法:awk 'BEGIN {FS="符号"} /模式/{动作}' 文件名
[root@server ~]# awk 'BEGIN{print $FS}'
# 输出账户的UID、GID
[root@server ~]# awk 'BEGIN{FS=":"} {print $3,$4}' /etc/passwd
[root@server ~]# awk '{print NF,NR,$0} END{print FILENAME}' awk1.txt
4 1 Li xiaoming xian 13311111111
4 2 zhang cunhua bapji 133222222222
4 3 wang xiaoer xianyang 13333333333
awk1.txt
[root@server ~]# awk '{print "第",NR,"行","有",NF,"列" > "/root/t1.txt"}' awk1.txt
[root@server ~]# cat t1.txt
第 1 行 有 4 列
第 2 行 有 4 列
第 3 行 有 4 列
与其他的程序设计语言一样,awk本身支持变量的相关操作,包括变量的定义和引用,以及参与相关的运算等。此外,还包含了许多内置的系统变量
变量的作用是用来存储数据。变量由变量名和值两部分组成,其中变量名是用来实现变量值的引用的途径,而变量值则是内存空间中存储的用户数据
awk的变量名只能包括字母、数字和下划线,并且不能以数字开头。例如abc、a_、_z以及a123都是合法的变量名,而123abc则是非法的变量名。另外,awk的变量名是区分大小写的,因此,X和x分别表示不同的变量
awk中的变量类型分为两种,分别为字符串和数值。但是在定义awk变量时,毋需指定变量类型,awk会根据变量所处的环境自动判断。如果没有指定值,数值类型的变量的缺省值为0,字符串类型的变量的缺省值为空串
变量 | 作用 |
---|---|
$0 | 记录变量,表示所有域 |
$n | 字段变量,表示第n个域 |
NF | 当前记录的域个数 |
NR | 显示每一行的行号 |
FS | 输入字段分隔符,默认值是空格或者制表符,可使用-F指定分隔符 |
OFS | 输出字段分隔符 ,OFS=”#”指定输出分割符为# |
RS | 记录分隔符,默认值是换行符 \n |
ENVIRON | 当前shell环境变量及其值的关联数组 |
[root@server ~]# vim awk2.txt
zhangsan 68 88 92 45 71
lisi 77 99 63 52 84
wangwu 61 80 93 77 81
[root@server ~]# vim test.awk
{
print
print "$0:",$0
print "$1:",$1
print "$2:",$2
print "NF:",NF
print "NR:",NR
print "FILENAME:",FILENAME
}
# 使用awk命令调用脚本执行
[root@server ~]# awk -f test.awk awk2.txt
zhangsan 68 88 92 45 71
$0: zhangsan 68 88 92 45 71
$1: zhangsan
$2: 68
NF: 6
NR: 1
FILENAME: awk2.txt
lisi 77 99 63 52 84
$0: lisi 77 99 63 52 84
$1: lisi
$2: 77
NF: 6
NR: 2
FILENAME: awk2.txt
wangwu 61 80 93 77 81
$0: wangwu 61 80 93 77 81
$1: wangwu
$2: 61
NF: 6
NR: 3
FILENAME: awk2.txt
[root@server ~]# awk -F ":" 'BEGIN{OFS="\t"} {print $1,$2}' /etc/passwd
# 面试题:打印/etc/sos/sos.conf中所有的空白行的行号
[root@server ~]# awk '/^$/{print NR}' /etc/sos/sos.conf
awk允许用户自定义自己的变量以便在程序代码中使用
变量名命名规则与大多数编程语言相同,只能使用字母、数字和下划线,且不能以数字开头
awk变量名称区分字符大小写
例
[root@server ~]# awk 'BEGIN{test="hello world" ; print test}' # 变量定义在BEGIN中
hello world
[root@server ~]# awk -v test="hello world" BEGIN'{ print test}' # 变量定义在-V参数后
hello world
+ - * / % ^(指数) **(指数)
[root@server ~]# awk 'BEGIN{x=2;y=3;print x+y,x-y,x*y,x/y,x^y,x**y}'
5 -1 6 0.666667 8 8
# 面试题:统计某目录下的文件占用存储空间字节数
[root@server ~]# ll /root | awk 'BEGIN{size=0} {size=size+$5} END{print size/1024,"KB"}'
= += /= *= %= ^=
[root@server ~]# awk 'BEGIN{a=5;a+=5;print a}'
10
[root@server ~]# awk 'BEGIN{a=5;a*=3+2;print a}'
25
条件表达式?表达式1:表达式2
条件运算符是一个三目运算符,条表成立,则表达式1为最终结果否则表达式2为最终结果
例
[root@server ~]# vim awk2.txt
zhangsan 68 88
lisi 77 99
wangwu 61 80
[root@server ~]# awk '{max=$2>$3?$2:$3 ; print NR,"max=",max}' awk2.txt
1 max= 88
2 max= 99
3 max= 80
&& || !
> < >= <= == != ~(匹配) !~(不匹配)
# 查询/etc/passwd文件中第三列小于10以下的信息,仅列出账户与uid
[root@server ~]# awk -F ":" '$3<10{print $1 , $3}' /etc/passwd
# 查看ip地址
[root@server ~]# ifconfig ens160 | awk 'NR==2{print $2}'
++ -- + - 等
[root@server ~]# awk 'BEGIN{a=0 ; print a++ , ++a }'
0 2
# 注意:awk变量为字符串变量时参与了算术操作,其值自动转为数值0
[root@server ~]# awk 'BEGIN{a="china" ; print a++ , ++a }'
0 2
[root@server ~]# awk 'BEGIN{a="ABABABAB" ; print a++ , ++a }'
0 2
[root@server ~]# awk '{++count ; print $0} END{print "账户数:" , count}' /etc/passwd
# 注意:count未赋初值参与算术运算时数值自动转为0
[root@server ~]# vim awk3.txt
liming 85
wangwei 99
zhangsan 68
[root@server ~]# awk '$2>80{print }' awk3.txt
[root@server ~]# awk '/^l/{print}' awk3.txt
liming 85
[root@server ~]# awk '/^l|z/{print}' awk3.txt
liming 85
zhangsan 68
[root@server ~]#
[root@server ~]# awk '/root/{print}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
[root@server ~]# awk '/^l/ && $2 > 80 {print}' awk3.txt
liming 85
if (expression)
{
语句1
语句2
……
}
else
{
语句3
语句4
……
}
[root@server ~]# vim if.awk
#!/bin/awk -f
{
if($2>=90)
{
print "优秀"
}
else
{
if($2>=80)
{
print "良好"
}
else
{
if($2>=60)
{
print "及格"
}
else
{
print "补考"
}
}
}
}
[root@server ~]# chmod +x if.awk
[root@server ~]# ./if.awk awk3.txt
良好
优秀
及格
[root@server ~]# cat awk3.txt
liming 85
wangwei 99
zhangsan 68
# 输出UID小于GID的账户名
[root@server ~]# awk 'BEGIN{FS=":"} {if($3<$4) print $1}' /etc/passwd
adm
lp
mail
games
ftp
# UID为奇数的账户名
[root@server ~]# awk 'BEGIN{FS=":"} {if($3%2==1) print $1}' /etc/passwd
# UID为偶数,但小于20的账户名
[root@server ~]# awk 'BEGIN{FS=":"} {if($3%2==0 && $3<20) print $1}' /etc/passwd
# uid包含9的账户名
[root@server ~]# awk 'BEGIN{FS=":"} {if($3~9) print $1}' /etc/passwd
# 面试题:
1.查询cpu占用率大于指定数值的信息
[root@server ~]# ps -eo user,pid,pcpu,comm | awk '{if($3>0) print}'
2.统计系统账户数(UID小于1000的为系统账户,其它为普通账户)
[root@server ~]# awk -F ":" '{if($3<1000) {x++} else {y++}} END{print "系统账户数:",x,"\n","普通账户数:",y}' /etc/passwd
系统账户数: 35
普通账户数: 2
格式与c语言格式相同
例
[root@server ~]# awk 'BEGIN{for(i=1;i<=100;i++) {sum=sum+i} ; print "sum=",sum}'
sum= 5050
while(expression)
{
循环语句1
循环语句2
……
}
do{
循环语句1
循环语句2
……
}while(expression)
[root@server ~]# awk 'BEGIN{while(i<=100) {sum+=i ; i++} ; print "sum=",sum}'
sum= 5050
[root@server ~]# awk -F ":" '{if($3%2==0) next ; print $1}' /etc/passwd
与c语言格式相同
printf("format\n",输出列表项)
%c:字符
%d,%i:十进制整数
%u:无符号整数
%f:浮点数
%e,%E:科学计数法
%s:字符串
%%:显示一个%
N:数字
-:左对齐
+:显示数值符号
[root@server ~]# awk 'BEGIN{printf("%d,%c\n","A",97)}'
0,a
[root@server ~]# awk 'BEGIN{printf("%5d\n",12345)}'
12345
[root@server ~]# awk 'BEGIN{printf("%2d\n",12345)}'
12345
[root@server ~]# awk 'BEGIN{printf("%5d\n",12)}'
12
[root@server ~]# awk 'BEGIN{printf("%-5d\n",12)}'
12
[root@server ~]# awk 'BEGIN{printf("%10.2f\n",123.4567)}'
123.46
[root@server ~]# awk 'BEGIN{printf("%5.2f\n",123.4567)}'
123.46
[root@server ~]# awk 'BEGIN{printf("%.2f\n",123.4567)}'
123.46
[root@server ~]# awk 'BEGIN{printf("%E\n",123.4567)}'
1.234567E+02
[root@server ~]# awk -F ":" '{printf("%-20s%d\n",$1,$3)}' /etc/passwd
# 面试题:计算本机内存的占用率%
[root@server ~]# free | awk 'NR==2{printf("内存利用率:%%%.2f\n",($3/$2)*100)}'
索引数组以数字作为下标
通过数组的下标(索引)引用数组中所有元素,下标一般从0开始
例:
[root@server ~]# awk 'BEGIN{a[0]="a" ; a[1]="b" ; a[2]="c" ; a[3]="d" ; print a[0],a[1],a[2],a[3]}'
a b c d
[root@server ~]# awk 'BEGIN{a[0]="a" ; a[1]="" ; a[2]="c" ; a[3]="d" ; print a[0],a[1],a[2],a[3]}'
a c d
[root@server ~]# awk 'BEGIN{a[0]="a" ; a[1]="b" ; a[2]="c" ; a[3]="d" ; print a[0],a[1],a[2],a[3],a[4]}'
a b c d
[root@server ~]# awk 'BEGIN{a["zero"]="a" ; a["one"]="b" ; a["two"]="c" ; a["three"]="d" ; print a["zero"],a["one"],a["two"],a["three"]}'
a b c d
for(初始化;条件表达式;步长)
{
循环体语句
}
for(变量 in 数组)
{
循环体语句
}
[root@server ~]# awk 'BEGIN{a[0]="a";a[1]="b";a[2]="c";a[3]="d"; for(i=0;i<4;i++) {print i,a[i]}}'
0 a
1 b
2 c
3 d
[root@server ~]# awk 'BEGIN{a["zero"]="a" ; a["one"]="b" ; a["two"]="c" ; a["three"]="d" ; for(i in a) {print i,a[i]}}'
three d
two c
zero a
one b
[root@server ~]# awk 'BEGIN{a=1;print ++a}'
2
[root@server ~]# awk 'BEGIN{a="test" ; print a,++a}'
test 1
# 准备文本
[root@server ~]# vim iptest.txt
192.168.48.1
192.168.48.2
192.168.48.5
192.168.48.1
192.168.48.3
192.168.48.1
192.168.48.5
192.168.48.2
192.168.48.4
192.168.48.1
[root@server ~]# awk '{count[$1]++} END{ for(i in count) {print i,"次数:",count[i]}}' iptest.txt
192.168.48.1 次数: 4
192.168.48.2 次数: 2
192.168.48.3 次数: 1
192.168.48.4 次数: 1
192.168.48.5 次数: 2
分析
例2:查看服务器连接状态并汇总
[root@server ~]# netstat -an | awk '/^tcp/{++s[$NF]} END{for(i in s) {print i,s[i]}}'
LISTEN 8
ESTABLISHED 2
<1>输出当前系统所有用户的UID:
# awk –F :’{print $3}’ /etc/passwd
注释:-F :指定分隔符为:$3指定第三段
<2>输出当前系统所有用户的UID,在首行加入UserUid:
# awk -F : 'BEGIN{print "UserUid"}{print $3}' /etc/passwd
<3>输出当前系统shell为/bin/bash的用户名,在最后一行加入END That is last line!!!
# awk -F : /bash$/'{print $1}END{print "END That is last line!!!"}'
/etc/passwd
<4>输出当前系统上GID为0的用户的用户名
# awk -F : '$4==0{print $1}' /etc/passwd
<5>输出当前系统上GID大于500的用户的用户名
# awk -F : '$4>500{print $1}' /etc/passwd
<6>输出当前系统上的所有用户名和UID,以“ # # ”为分隔符
# awk -F: 'OFS=" # # "{print $1,$3}' /etc/passwd
<7>输出/etc/passwd文件中以“:”为分隔符的最后一段。
# awk -F: '{print $NF}' /etc/passwd
<8>对/etc/passwd文件中输出的每一行计数
# awk '{print NR,$0}' /etc/passwd
<9>对/etc/passwd、/etc/fstab文件中输出的每一行分别计数。
# awk '{print FNR,$0}' /etc/passwd /etc/fstab
<10>自定义变量
# awk -v var="Linux.com.cn" BEGIN'{print var}'
<11>以printf格式输出用户名,UID、GID
# awk -F: '{printf "%-15s %d %8i\n",$1,$3,$4}' /etc/passwd
<12>检测当前系统上所有用户,如果用户名为root输出:Admin
如果用户名不为root输出:Common User
# awk -F: '{if ($1=="root") printf "%-15s: %s\n", $1,"Admin"; else printf
"%-15s: %s\n", $1, "Common User"}' /etc/passwd
<13> 统计当前系统上UID大于500的用户的个数
# awk -F: -v sum=0 '{if ($3>=500) sum++} END{print sum}' /etc/passwd
<14>读取/etc/passwd文件中的每一行的每一个字段,输出每个字段中字符个数大于等于四的字段。
# awk -F: '{i=1;while (i<=NF) { if (length($i)>=4) {print $i}; i++ }}'
/etc/passwd
<15>使用do-while语句输出/etc/passwd中每一行中的前三个字段
# awk -F: '{i=1;do {print $i;i++}while(i<=3)}' /etc/passwd
<16>使用for语句输出/etc/passwd中每一行中的前三个字段
# awk -F: '{for(i=1;i<=3;i++) print $i}' /etc/passwd
<17>统计/etc/passwd文件中各种shell的个数
# awk -F: '$NF!~/^$/{BASHsum[$NF]++}END{for(A in BASHsum){printf
"%-15s:%i\n",A,BASHsum[A]}}' /etc/passwd
注释:
$NF!~/^$/:最后一个字段非空
BASHsum[$NF]++:最后一个字段相同的加一
<18> 显示当前系统上UID号为偶数的用户名和UID
# awk -F: '{if($3%2==1) next;{printf "%-15s%d\n",$1,$3}}' /etc/passwd
<19> 统计当前系统上以tcp协议工作的各端口的状态数
# netstat -ant | awk '/^tcp/ {++STATE[$NF]} END {for(a in STATE) print a,
STATE[a]}'
<20>输出/etc/passwd中的每一行以||||隔开,默认不换行
# awk -F: 'BEGIN{ORS="||||"}{print $0}' /etc/passwd