AWK
熟悉linux系统的都知道,linux文本处理三剑客,grep,sed,awk。这三个工具中属awk最难理解与使用,以前自己也是只会基本使用,这里想总结下awk的基本使用,为了更好的理解与使用awk。
首先awk是一款文本报告生成工具,它不仅是linux也是任何环境中现有的功能最强大的数据处理引擎,它自成一套编程数据操作语言,效仿c语言设计理念,所以在语法上会与c语言很相近,awk提供了其强大的功能:可以进行样式装入、流控制、数学运算符、进程控制语句甚至于内置的变量和函数。它具备了一个完整的语言所应具有的几乎所有精美特性。
最简单地说, awk是一种用于处理文本的编程语言工具。awk在很多方面类似于 shell 编程语言,尽管awk具有完全属于其本身的语法,操作可能会很复杂,但命令的语法始终是:
1 | awk [opotion] 'program' file ... |
这里的program包括PATTERN{ACTION STATEMENTS}
选项
- -F:输入数据字段的分隔符
- -v var=value:自定义变量
常见的及常用的也就这两个选项了,示例:
1 | ~]#awk -F':' -v user='用户名' '{print user,$1}' /etc/passwd |
2 | 用户名 root |
3 | 用户名 bin |
4 | 用户名 daemon |
5 | 用户名 adm |
6 | ... |
变量
选项中我们可以自定义变量,而这里所需要了解的是内建变量:
- FS:输入的字段间分隔符
- OFS:输出的字段间分隔符
- RS:输入时的换行符
- ORS:输出时的换行符
- NF:字段的数量
- NR:行数
- FNR:各文件分别计行数
- FILENAME:当前参数的个数
- ARGC:命令行参数的个数
- ARGV:数组,保存的是命令行给定的各参数
示例:
输出当前系统的用户名:
1 | ~]#awk -v FS=':' -v user='用户名' '{print user,$1}' /etc/passwd |
输出当前系统的用户名,并以冒号隔开:
1 | ~]#awk -v FS=':' -v user='用户名' '{print "user:"$1}' /etc/passwd |
2 | |
3 | ~]#awk -v FS=':' -v user='用户名' 'BEGIN{OFS=":"}{print user,$1}' /etc/passwd |
修改输入换行符
1 | [root@localhost ~]#awk -v FS=':' -v user='用户名' 'BEGIN{RS= " "}{print user,$1}' /etc/passwd |
修改输出的换行符
1 | ~]#awk -v FS=':' -v user='用户名' 'BEGIN{ORS=" ";OFS=":"}{print user,$1}' /etc/passwd |
2 | 用户名:root 用户名:bin 用户名:daemon 用户名:adm 用户名:lp ... |
输出字段数
1 | ~]#awk -F":" '{print NF}' /etc/passwd |
2 | 7 |
3 | 7 |
4 | ... 输出的为每行以:隔开共有的字段数 |
5 | |
6 | ~]#awk -F":" '{print $NF}' /etc/passwd |
7 | /bin/bash |
8 | /sbin/nologin |
9 | /sbin/nologin |
10 | /sbin/nologin |
11 | /sbin/nologin |
12 | /bin/sync |
13 | ... 输出最后字段的内容 |
行数统计
1 | ~]#awk '{print NR}' /etc/fstab /etc/issue |
2 | 1 |
3 | 2 |
4 | 3 |
5 | 4 |
6 | 5 |
7 | 6 |
8 | 7 |
9 | 8 |
10 | 9 |
11 | 10 |
12 | 11 |
13 | 12 |
14 | 13 |
15 | 14 |
16 | 15 |
17 | [~]#awk '{print FNR}' /etc/fstab /etc/issue |
18 | 1 |
19 | 2 |
20 | 3 |
21 | 4 |
22 | 5 |
23 | 6 |
24 | 7 |
25 | 8 |
26 | 9 |
27 | 10 |
28 | 11 |
29 | 12 |
30 | 1 |
31 | 2 |
32 | 3 可以很清楚的看出NR和FNR的差别 |
参数的查看
1 | ~]#awk 'END{print ARGC}' /etc/fstab |
2 | 2 |
3 | |
4 | ~]#awk 'END{print ARGV[1]}' /etc/fstab |
5 | /etc/fstab |
6 | |
7 | ~]#awk 'END{print ARGV[0]}' /etc/fstab |
8 | awk |
格式化输出
printf format,item1,item2…
- format必须给出
- 不会自动换行
- format中需要分别为后面的每个item制定一个占位符
格式化符号
- %c:显示字符串的ascii码
- %d,%i:显示十进制整数
- %e,%E:科学计数法数值显示
- %f,显示为浮点型
- %g,%G:以科学计数法或浮点型形式显示数值
- %s:显示字符串
- %u:无字符串
- %%:显示%本身
修饰符
- #[.#]:第一个数字控制显示的宽度,第二个#表示小数点后的精度: %3.1f
- —:减号,左对齐
- +:显示数值的符号
示例:
1 | [root@localhost ~]#awk -F: '{printf "%s\n",$1}' /etc/passwd |
2 | root |
3 | bin |
4 | daemon |
5 | adm |
6 | ... |
1 | ~]#awk -F: '{printf "%20s %-d\n",$1,$3}' /etc/passwd |
2 | root 0 |
3 | bin 1 |
4 | daemon 2 |
5 | adm 3 |
6 | lp 4 |
7 | sync 5 |
8 | shutdown 6 |
9 | ... |
操作符
算数操作符:
x+y,x-y,x*y,x/y,x^y,x%y
-x +x:转换为数值
字符串操作符:没有符号的操作符,字符操作符
赋值操作符:
=,+=,-=,*=,/=,%=,^=
++,–
比较操作符:
,>=,<,<=,!=,==
模式匹配符:
~:是否匹配 !~:是否不匹配
逻辑操作符 && || !
条件表达式
selector?if-true-expression:if-false-expression
eg:
1 | awk -F: '{$3>=1000?usertype="common user":usertype="system admin";printf "%15s:%-s\n",$1,usertype}' /etc/passwd |
2 | awk -F: '{printf "%15s:%-s",$1,($3>1000?"common user":"system user")}' /etc/passwd |
pattern
- (1)empty 空模式,匹配每一行
- (2)/regular expression/:仅处理能够被此处的模式匹配到的行
- (3)relational expression:关系表达式,结果有“真”有“假”,结果为真才会处理
eg:
1 | awk -F':' '$NF=="/bin/bash" {print $1,$NF}' /etc/passwd |
2 | awk -F':' '$NF~/bash$/ {print $1,$NF}' /etc/passwd |
line range:行范围
/pat1/,/pat2/
1 | awk -F':' '/^root/,/^lp/{print $1}' /etc/passwd |
注意;不支持直接给出数字的格式
如果需要处理行数据可以使用下面的方法:
1 | ~]# awk -F : '(NR>=2&&NR<=10){print $1}' /etc/passwd |
BEGIN/END模式
BEGIN{}:仅在开始处理文件中文本之前执行一次
END{}:仅在文本处理完成之后执行一次
控制语句
- if(condition){statment}
- if(condition){statment} else {statement}
- while(condition){statement}
- do{statement} while(codition)
- for(exp1,exp2,exp3){statement}
- break
- continue
- delete arry[index]
- delete arry
- exit
- {statement}
if-else
语法:if(condition) statement [else statement]
1 | awk -F: '{if($3>1000) print $1,$3}' /etc/passwd |
2 | awk -F: '{if($3>=1000) {printf "common user: %s\n",$1} else {printf "root or system:%s\n",$1} }' /etc/passwd |
使用场景:对awk取得的整行或某个字段做条件判断;
while循环
语法:while(condition) statement
使用场景:对一行内的多个字段逐一类似处理时使用,对数组中逐一处理时使用
1 | awk '/^[[:space:]]*linux16/{i=1;while(i<=NF) {print $i,length($i); i++}}' /boot/grub2/grub.cfg |
2 | awk '/^[[:space:]]*linux16/{i=1;while(i<=NF) {if(length($i)>7){print $i,length($i)}; i++}}' /boot/grub2/grub.cfg |
do-wile循环
语法:do statement while(condition)
1 | awk -F: '{i=1;do{print $i;i++}while(i<=3)}' /etc/passwd |
意义;至少执行一次循环
for循环
语法:for(epr1;epr2;epr3) statement
1 | awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /boot/grub2/grub.cfg |
2 | awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {if(length($i) > 7){print $i,length($i)}}}' /boot/grub2/grub.cfg |
特殊用法:
能够遍历数组中的元素:
语法: for(var in arry) (for-body)
switch语句
语法:switch(expression){case VALUE or /REGEXP/:statement;case VALUE2 or /REGEXP2/: statement;…;default:statement}
break和continue
break [n] continue
next
提前结束对本行的处理而直接进入下一行
1 | awk -F: '{if($3%2!=0) next;print $1,$3}' /etc/passwd |
awk 数组
关联数组:array[index-expression]
index-expression:
(1)可使用任意字符串,字符串要加双引号
(2)如果某组元素事先不存在,在引用时,awk会自动创建此元素,并将某值初始化为“空串”:
若要判断数组中是否存在某元素,要使用“index in array”格式进行
若要遍历数组中的每个元素,要使用for循环
1 | awk 'BEGIN{weekdays["mon"]="monday";weekdays["tue"]="tuesday";for(i in weekdays) {print weekdays[i]}}' |
注意:var会遍历array的每个索引
1 | netstat -nta | awk '/^tcp\>/{state[$NF]++}END{for(i in state){print i,state[i]}}' |
2 | netstat -ant | awk '/^tcp\>/{split($5,ip,":");count[ip[1]]++}END{for(i in count) {print i,count[i]}}' | sort -k 2 -t " " -nr |
统计指定文件中每个单词出现的次数
1 | awk '{for(i=1;i<=NF;i++){count[$i]++}}END{for(i in count){print i,count[i]}}' /etc/fstab |
函数
内置函数:rand();返回0和1之间的一个随机数
字符串处理:
length(s):返回指定字符串的长度
sub(r,s,[t]): 以r表示的模式来查找t所表示的字符串匹配的内容,并将其第一次出现替换为s所表示的内容
gsub(r,s,[t]):以r表示的模式来查找t所表示的字符串匹配的内容,并将其所有出现替换为s所表示的内容
split(s,a[,r]): 已r为分隔符切割字符s,并将切割后的结果保存至a所表示的数组中:
1 | netstat -ntl | awk '/^tcp\>/{split($5,ip,":");count[ip[1]]++}END{for(i in count) {print i,count[i]}}' |
system(command):执行系统command并将结果返回至awk命令
1 | ~]#awk '{print system(ifconfig)}' |
2 | |
3 | 0 |
4 | 只返回执行结果 |
systime():取系统当前时间
1 | ~]#awk '{print systime()}' |
2 | |
3 | 1488510637 |
tolower(s):将s中的所有字母转为小写
1 | ~]#awk -v s='SDADSD' '{print tolower(s)}' |
2 | |
3 | sdadsd |
toupper(s):将s中的所有字母转为大写
1 | ~]#awk -v s='sssss' '{print toupper(s)}' |
2 | |
3 | SSSSS |