gawk入门及进阶
GNU awk
文本处理三工具:
grep 、egrep、fgrep :文本过滤工具,pattern
sed :行编辑器
模式空间 、保持空间
awk :报表生成器,格式化文本输出;
AWK :Aho,Weinberger,Kernighan,三位作者,在Unix上使用
New AWK :NAWK
gwak :GNU awk ,在Linux上使用
gawk : pattern scanning and processing language
基本用法 :gawk [option] 'program' FILE ...
program :PATTERN{ACTION STATEMENTS}
语句之间用分号分隔
选项:
-F :指明输入时用到的字段分隔符;
-v var=value :自定义变量;
1. print
print item1,item2,...
要点:
(1) 逗号作为分隔符,默认输出结果的分隔符是一个空白字符;
(2) 输出的各item可以是字符串,也可以是数值,当前记录的字段、变量或awk的表达式;
(3) 如省略item,相当于print $0,即输出全部;
2. 变量:【注意】引用变量不要加$符号
2.1 内建变量
FS :input field seperator,默认为空白字符;
OFS :output field seperator,默认为空白字符;
如:]# awk -v FS=':' -v OFS=',' '{print $1,$2}' /etc/passwd
]# awk -v RS=':' '{print}' /etc/passwd
RS :input record seperator,输入时的换行符;
ORS :output record seperator,输出时的换行符;
如:]# awk -v RS=':' -v ORS=' ' '{print}' /etc/passwd
root x 0 0 root /root /bin/bash
bin x 1 1 bin /bin /sbin/nologin
NF :number of field,字段数量
如:]# awk -v OFS='********' '{print NF,$NF,$0}'
【注意】引用变量不要加$符号:
此处{print NF}与{print $NF} 二者的输出效果不同,NF代表本行有多少个字段,$NF代表将NF的值作为变量名。
最终的执行效果为:输出本行的字段数量和本行最后一个字段。
NR :number record , 处理的行数,如果有多个文件则一起计数
FNR:file number recor ,多个文件分别计数
例1 :输出处理两个文件的行数,两个文件行数一起计算
]# awk '{print NR}' /etc/fstab /etc/issue
例2 :输出处理两个文件的行数,两个文件行数单独分别计算
]# awk '{print FNR}' /etc/fstab /etc/issue
FILENAME : 当前处理的文件的文件名
例1 :]# awk '{print FNR,FILENAME}' /etc/fstab /etc/issue
ARGC :命令行参数的个数;
ARGV :数组,保存的是命令行所给定的各参数;
如:
]# awk 'BEGIN{print ARGC}' /etc/fstab /etc/issue
3
]# awk 'BEGIN{print ARGV[0]}' /etc/fstab /etc/issue
awk
]# awk 'BEGIN{print ARGV[1]}' /etc/fstab /etc/issue
/etc/fstab
]# awk 'BEGIN{print ARGV[2]}' /etc/fstab /etc/issue
/etc/issue
2.2 自定义变量 【注意】引用变量不要加$符号
(1) -v var=value
变量名区分字符大小写:
如:]# awk -v test="hello awk" 'BEGIN{print test}'
hello awk
(2) 在program中直接定义
如:]# awk 'BEGIN{test="hello awk";print test}'
hello awk
3. printf 命令
格式化输出 :printf FORMAT,item1,item2,...
(1) FORMAT必须给出;
(2) 不会自动换行,需要显式给出换行控制符,\n
(3) FORMAT中需要分别为后面的每个item指定一个格式化符号;
格式符:
%c :显示字符的ASCII码
%d,%i:显示十进制整数
%e,%E :科学计数法数值显示
%f :显示为浮点数
%g,%G : 以科学计数法或浮点形式显示数值
%s :显示字符串
%u :无符号整数
%% :显示%自身
如:]# awk -F: '{printf "Username: %s---UID:%d\n",$1,$3}' /etc/passwd
Username: root---UID:0
Username: bin---UID:1
修饰符:在格式符前面加修饰的符号,控制格式的显示机制
#[.#]:第一个数字控制显示的宽度(默认右对齐):第二个#标识小数点后的精度(可省略)
%3.1f
如:
]# awk -F: '{printf "Username:%10s UUID:%7.1f\n",$1,$3}' /etc/passwd
Username: ntp UUID: 38.0
Username: abrt UUID: 173.0
- :代表左对齐
如:
]# awk -F: '{printf "Username: %-10sUUID: %5.1f\n",$1,$3}' /etc/passwd
Username: ntp UUID: 38.0
Username: abrt UUID: 173.0
+ :显示数值的正负符号
4. 操作符
算术操作符:
x+y,x-y,x*y,x/y,x^y,x%y :
-x :单目操作符,变为负数
+x :转换为数值
字符串操作符:没有符号的操作符表示字符串连接
赋值操作符:
=,+=,-=,*=,/=,^=,%=
++,-- :自加,自减
比较操作符:
>,>=,<,<=,!=,==
模式匹配符:
~ :匹配
!~ :是否不匹配
逻辑操作符:
&&
||
!
函数调用:
function_name(arg1,arg2,...)
条件表达式:
selector?if-true-expression:if-false-expression
两个expression间用冒号:分隔
例如:]# awk -F':' '{$3>499?usertype="common user":usertype="sys user";printf "%-10s %5d %s\n",$1,$3,usertype}' /etc/passwd
5. pattern
(1) empty :空模式,匹配每一行
(2) /regular expression/ :仅对能被此处模式匹配到的行
如:]# awk '/nologin$/{print NR,$0}' /etc/passwd
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
【注意】此处不能print $1 。
(3) relational expression :关系表达式,结果为布尔型值,即真和假;结果为真才能被匹配处理;
真:结果为非0值,非空字符串
假:结果为0,空字符串
例1:
]# awk -F: '$3>499{printf "username:%-10s UID:%3d\n",$1,$3}' /etc/passwd
username:achuDk UID:500
username:gentoo UID:501
例2:
]# awk -F: '$NF~/bash$/{printf "linenum:%-3d username:%-6s bash:%d\n",NR,$1,$NF}' /etc/passwd
linenum:1 username:root bash:0
linenum:20 username:achuDk bash:0
(4) line ranges :行范围
startline,endline : /pat1/,/pat2/
【注意】不支持直接给出数字的格式
例如:
]# awk -F: '/^r/,/^h/{SUM=$3+$4;printf "Line record: %-2d Uname: %10s UID: %3d GID: %3d $3+$4: %3d\n",NR,$1,$3,$4,SUM}' /etc/passwd
Line record: 4 Uname: adm UID: 3 GID: 4 $3+$4: 7
Line record: 5 Uname: lp UID: 4 GID: 7 $3+$4: 11
例如:
]# awk -F: 'NR>=3&&NR<=10{print}' /etc/passwd
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
(5) BEGIN/END 模式:
BEGIN{} :仅在开始处理文件中的文本之前执行一次,如:输出表头
END{} :仅在文本处理完成之后,命令结束之前执行一次;
(6) 常用的action
1) Expression
2) Control statements : 控制语句:if,while等;
3) Compound statements : 组合语句;
4) input statements :输入语句
5) output statements :输出语句
(7) Control statements 控制语句
if(condition) {statements}
if(condition) {statements} else {statements}
while(condition) {statements}
do {statements} while(condition)
for(expr1;expr2;expr3) {statements}
break
continue
delete array[index]
delete array
exit
{ statements }
7.1 if-else
使用场景:对awk取得的整行或某个字段做条件判断;
语法:
单分支:'{if(condition) statement}'
多分支:'{if(condition) {statement} else {statement}}'
例1:找出/etc/passwd目录下UID<=100的用户打印Uname和UID:
单分支语句示例:
]# awk -F: '{if($3<=10)print $1,$3}' /etc/passwd
多分支语句示例:
例1:]# awk -F: '{if($3<=499){printf "Super user: %s\n",$1}else{printf "Common user: %s\n",$1}}' /etc/passwd
例2:]# awk -F: '{if($NF=="/bin/bash") {print NR,$1}else {print $1,$NF}}' /etc/passwd
如:显示磁盘空间利用率大于2%的磁盘
]# df | awk '/^\/dev/{print $1,$6}' | awk -F% '{print $1}' | awk '{if($2>=2) printf "The used-percent of this partition has over %%2:%s \n",$1}'
The used-percent of this partition has over %2:/dev/sda2
The used-percent of this partition has over %2:/dev/sda1
7.2 while 循环
语法:while(conditiont) statement
条件为“真”,进入循环;条件为“假”,退出循环;
使用场景:对【一行内的多个字段】或对【数组中的各元素】逐一进行类似处理时使用,因为awk已经对整个文件的所有行进行循环;
示例:显示某文件中所有符合条件的行,对行内的每一个字段,显示字段本身和字段内字符的个数:
lenth(WORDS_NAME) :长度函数,显示某字段内字符的个数
结果:]# awk '/^[[:space:]]+kernel/{i=1;while(i<=NF) {print $i,length($i);i++}}' /etc/grub.conf
kernel 6
/vmlinuz-2.6.32-642.el6.x86_64 30
ro 2
root=UUID=aadac335-a5b8-40ef-bdbe-ecb0fcc09d01 46
rd_NO_LUKS 10
rd_NO_LVM 9
LANG=en_US.UTF-8 16
rd_NO_MD 8
SYSFONT=latarcyrheb-sun16 25
crashkernel=auto 16
KEYBOARDTYPE=pc 15
KEYTABLE=us 11
rd_NO_DM 8
扩展条件:设定条件:仅显示length($i)>=6的字段
]# awk '/^[[:space:]]+kernel/{i=1;while(i<=NF) {if(length($i)>=6) {print $i,length($i)};i++}}' /etc/grub.conf
kernel 6
/vmlinuz-2.6.32-642.el6.x86_64 30
7.3 do-while循环
语法:do statemnet while(condition)
意义:至少执行一次循环体,即使condition为假
7.4 for循环
语法:for(expr1;expr2;expr3) statement
expr1 :控制变量初始化
expr2 :条件判断
expr3 :控制变量修正表达式
for-body :循环体
for(variable assignment;condition;iteration process) {for-body}
示例:显示某文件中所有符合条件的行,对行内的每一个字段,显示字段本身和字段内字符的个数:使用length()函数。
结果:]# awk '/^[[:space:]]+kernel/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /etc/grub.conf
特殊用法:
能够遍历数组中的元素:
语法:for(var in array) {for-body}
7.5 switch语句 类似于bash中的case语句
语法:swtich(expression) {case VALUE1 or /REGEXP1/: statement; case VALUE2 or /REGEXP2/: statement; ...; default: statement}
7.6 break和continue 仅针对某一行内多个字段间的循环进行处理
break [n] :跳出第n层循环
continue :提前结束本轮循环
7.7 next
提前结束对本行的处理,直接进入下一行的处理
示例:跳过UID号为奇数的行,仅显示UID号为偶数的用户
结果:]# awk -F: '{if($3%2!=0) {next} else {print $1,$3}}' /etc/passwd
root 0
daemon 2
8. array 数组
关联数组 :array[index-expression]
index-expression 类型
(1) 可使用任意字符串:
(2) 如果某数组元素事先不存在,在引用时,awk会自动创建此元素,并将其值初始化为“空串”;
【注意】
1、可使用任意字符串,字符串要使用双引号;
2、如果想判断数组中是否存在某元素或某元素是否为空时,要使用“index in array”格式进行,千万不要通过引用这个元素来判断
weekdays["mon"]="Monday"
例如:
]# awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";print weekdays["mon"],weekdays["tue"]}'
Monday Tuesday
要遍历数组中的每个元素,要使用for循环;
for(var in array) {for-body}
例1:
]# awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";for(i in weekdays) {print weekdays[i]}}'
Monday
Tuesday
【注意】var会遍历array的每个索引;
例2:统计tcp协议的所有端口的连接状态:
]# netstat -tnl | awk '{print $4,$0}' | awk '/^[[:digit:]]+/{state[$NF]++}END{for(i in state) {print i,state[i]}}'
LISTEN 3
例3:统计所有进程中以root身份运行的进程
]# ps aux | awk '{user[$1]++}END{for(i in user);{print i,user[i]}}'
root 141
例4:统计/etc/fstab中所有fs出现的频率分布
]# cat /etc/fstab | awk '/^[[:alnum:]]/{fstype[$3]++}END{for(i in fstype) {printf "fs-type: %-10s distribution: %d \n",i,fstype[i]}}'
fs-type: devpts distribution: 1
fs-type: swap distribution: 1
fs-type: sysfs distribution: 1
fs-type: proc distribution: 1
fs-type: tmpfs distribution: 1
fs-type: ext4 distribution: 4
* 例5:统计文件中每个单词出现的次数:
]# awk -F: ‘{for(i=1;i<=NF;i++){words[$i]++}}END{for(i in words) {print i,words[i]}}’ /etc/passwd | sort -k 2 -nr
]# awk '{for(i=1;i<=NF;i++){fstype[$i]++}}END{for(i in fstype) {print i,fstype[i]}}' /etc/fstab
9. 函数
9.1 内置函数
rand() :返回0和1之间的随机数
如:]# awk 'BEGIN{print rand()}'
0.237788
【注意】只有第一次取是随机的,以后都会成为定值
字符串处理:
length([s]) :返回指定字符串的长度
sub(r,s,[t]) :以r表示的模式来查找t所表示的字符串中的匹配的内容,并将其第一次出现替 换为s所表示的内容;t可以省略
gsub(r,s,[t]) :全局替换
split(s,a[,r]) :以r为分隔符切割字符s,并将切割后的结果保存至a所表示的数组中;r可以省略
例如:统计netstat信息中的local address 去除端口号的数据:
]# netstat -tnl | awk '/^tcp/{split($4,ip,":");count[ip[1]]++}END{for(i in count) {print i,count[i]}}'
3
127.0.0.1 1
0.0.0.0 2