awk [options] 'program' FILE ...
其中:
options:
-F:指明输入时用到的字段分隔符,默认空白字符;
-v:指定变量 -v var=value:自定义变量
program:
PATTERN{ ACTION STATEMENTS;...} (语句之间用 ; 分隔)
PATTERN--模式,这意味着并不是对文件中的每一行进行处理,而是处理那些能够被模式匹配到的行,不跟模式表示全文;
ACTION--常见的处理机制是打印,命令有print和printf.
1、内置变量
FS:切割时的分隔符,默认为空白字符;
OFS:切割后输出时的分隔符,默认为空白字符;
RS:输入时的换行符,默认为'\n';
ORS:输出时的换行符,默认为'\n';
NF:字段数量 ( $NF表示最后一个字段);
NR:行数;
FNR:分别计算各文件的行数;
FILENAME:当前文件名;
ARGC:命令行参数的个数;
ARGV:数组,保存的是命令行所给定的各参数
示例:
awk -v FS=: '{print $1}' /etc/passwd (相当于awk -F: "{print $1}" /etc/passwd)
awk -v FS=: -v OFS=, '{print $1,$2}' /etc/passwd
awk -v RS=' ' '{print}' /etc/issue
awk '{print NF}' /etc/fstab (可对比 awk '{print $NF}' /etc/fstab,$NF表示最后一个字段)
awk 'BEGIN{print ARGC}' /etc/fstab /etc/issue (命令行参数有3个,分别为awk,/etc/fstab,/etc/issue)
awk 'BEGIN{print ARGV[#]}' /etc/fstab /etc/issue (#表示索引下标)
2、自定义变量
两种方式可自定义变量:
(1) -v var=value
(2) 在program中直接定义
示例:
awk -v test="hello gawk" 'BEGIN{print test}' (相当于awk 'BEGIN{test="hello gawk";print test}')
3、打印输出
print item1,item2,...
示例:
tail -5 /etc/fstab | awk '{print $2,$4}' ($1..$#:内置变量,表示分隔后的字段)
tail -5 /etc/fstab | awk '{print "hello:",$2,$4}' (注意,{}只能由单引号'' 引起来,不能用双引号"")
要点:
(1) 逗号,作为不同字段的分隔符;
(2) item可以是字符串,数值,当前记录的字段、变量或awk的表达式;
(3) 如省略item,相当于print $0(打印所有元素;$0表示所有字段)
printf
格式化输出:printf FORMAT,item1,item2,...
释义:
(1) FORMAT必须给出;
(2) 不会自动换行,需要显式给出换行控制符,'\n';
(3) FORMAT中需要分别为后面的每个item指定一个格式化符号;
格式符:
%c:显示字符的ASCII码;
%d,%i:显示十进制整数;
%e,%E:科学计数法数值显示;
%f:显示为浮点数;
%g,%G:以科学计数法或浮点形式显示数值;
%s:显示字符串;
%u:无符号整数;
%%:显示%自身
修饰符:
#[.#]:第一个数字控制显示的宽度;第二个#表示小数点后的精度
示例:
%-3.1f,其中 '-' 表示左对齐;%+5.2d,其中 '+' 会显示数值的符号
awk -F: '{printf "Uername:%s, UID:%d\n",$1,$3}' /etc/passwd
awk -F: '{printf "Uername:%-15s, UID:%d\n",$1,$3}' /etc/passwd
4、操作符
算术操作符:x+y,x-y,x*y,x/y,x^y(多少次方),x%y
赋值操作符:=,+=,-=,*=,/=,%=,^=,++,--
比较操作符:>,>=,<,<=,!=,==
模式匹配符:~:是否匹配;!~:是否不匹配
逻辑操作符:&&,||,!
函数调用:function_name(argu1,argu2,...)
条件表达式:selector ? if-true-expression : if-false-expression
示例:
awk -F: '{$3>=1000?usertype="Common User":usertype="Sysadmin or SysUser";printf "%15s : %-s\n",$1,usertype}' /etc/passwd
awk '!/^UUID/{print $0}' /etc/fstab
awk -F: '$3>1000{print $1,$3}' /etc/passwd
awk -F: '$NF~/bash$/{print $1,$NF}' /etc/passwd
5、pattern
(1) /regular expression/:仅处理被模式匹配到的行;
(2) relational expression:关系表达式,为"真"时处理 ("真":结果是非0值或非空字符串);
(3) line ranges:行范围 (startline, endline 或 /pat1/, /pat2/);
注意: 此处行范围不支持直接给出数字的格式
示例:
awk -F: '(NR>=2&&NR<=10){print $1}' /etc/passwd
awk -F: '/^h/,/^s/{print $1}' /etc/passwd
(4) BEGIN/END模式:
BEGIN{}: 仅在开始处理文件中的文本之前执行一次;
END{}:仅在文本处理完成之后执行一次
示例:
head /etc/passwd | awk -F: 'BEGIN{print "username uid"}{printf "%-12s%-5s\n",$1,$3}END{printf "%10s\n","END"}'
1、if-else
语法:
if(condition) statement [else statement]
使用场景:
对awk取得的整行或某个字段做条件判断.
示例:
awk -F: '{if($3>=1000) {printf "Common user: %s\n",$1} else {printf "root or Sysuser: %s\n",$1}}' /etc/passwd
awk -F: '{if($NF=="/bin/bash") print $1}' /etc/passwd
awk '{if(NF>5) print $0}' /etc/fstab
df -h | awk -F% '/^\/dev/{print $1}' | awk '{if($NF>=20) print $1}'
2、while and do while
语法:
while(condition) statement (条件"真"时进入循环;条件"假"时退出循环)
do statement while(condition) (至少执行一次循环体)
使用场景:
对一行内的多个字段逐一处理时使用;对数组中的各元素逐一处理时使用.
示例:
awk '/^[[:space:]]*linux16/{i=1;while(i<=NF) {print $i,length($i); i++}}' /etc/grub2.cfg
awk '/^[[:space:]]*linux16/{i=1;while(i<=NF) {if(length($i)>=7) {print $i,length($i)}; i++}}' /etc/grub2.cfg
3、for
语法:
for(expr1;expr2;expr3) statement
for(variable assignment;condition;iteration process) {for-body}
示例:
awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /etc/grub2.cfg
特殊用法:遍历数组中的元素
语法:for(var in array) {for-body}
4、next
提前结束对本行的处理而直接进入下一行;
示例:
awk -F: '{if($3%2!=0) next; print $1,$3}' /etc/passwd
5、array 数组
1) 关联数组:
array[index-expression]
index-expression:
① 可使用任意字符串;字符串要使用双引号;
② 如果某数组元素事先不存在,在引用时,awk会自动创建此元素,并将其值初始化为"空串"
2) 若要判断数组中是否存在某元素,要使用"index in array"格式进行;
3) 若要遍历数组中的每个元素,要使用for循环:for(var in array) {for-body}
示例:
awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";for(i in weekdays) {print weekdays[i]}}'
netstat -tan | awk '/^tcp\>/{state[$NF]++}END{for(i in state){ print i,state[i]}}'
awk '{ip[$1]++}END{for(i in ip){print i,ip[i]}}' /var/log/httpd/access_log
awk '/^UUID/{fs[$3]++}END{for(i in fs){print i,fs[i]}}' /etc/fstab
awk '{for(i=1;i<=NF;i++){count[$i]++}}END{for(i in count){print i,count[i]}}' /etc/fstab
netstat -tan | awk '/^tcp\>/{state[$NF]++}END{for(i in state){print i,state[i]}}'
四、内置函数
awk为程序员们提供了非常丰富的内置函数
算数函数
- rand():rand函数可以生成随机数,但是在使用rand函数时需要配合srand函数,否则rand函数返回的值将一直不变。
- srand():配合rand函数使用,生成随机数。
- int():截取数字整数部分的值。
(1)rand()
[zkpk@master as]$ awk 'BEGIN{print rand()}'
0.237788
[zkpk@master as]$ awk 'BEGIN{print rand()}'
0.237788
[zkpk@master as]$ awk 'BEGIN{print rand()}'
0.237788
[zkpk@master as]$ awk 'BEGIN{print rand()}'
0.237788
#上面示例可以说明rand()产生随机数,但之后一直不变(rand每次产生一个0~1之间的随机数且不包含0或1)
(2)srand()
[zkpk@master as]$ awk 'BEGIN{srand();print rand()}'
0.197708
[zkpk@master as]$ awk 'BEGIN{srand();print rand()}'
0.659169
[zkpk@master as]$ awk 'BEGIN{srand();print rand()}'
0.659169
[zkpk@master as]$ awk 'BEGIN{srand();print rand()}'
0.652366
#配合srand函数再使用rand函数产生的随机数每次都不一样
(3)int
[zkpk@master as]$ awk 'BEGIN{srand();print int(100*rand())}'
4
[zkpk@master as]$ awk 'BEGIN{srand();print int(100*rand())}'
53
[zkpk@master as]$ awk 'BEGIN{srand();print int(100*rand())}'
96
[zkpk@master as]$ awk 'BEGIN{srand();print int(100*rand())}'
24
#使用int函数取其整数部分
3.字符串函数
- gsub():会替换指定范围内所有符合条件的字符
- sub(): 只会替换指定范围内第一次匹配符合条件的字符
- length():获取字符串的长度
- index():获取指定字符位于字符串的位置
- split():可以将指定字符串分割,将切割后的每一段赋值到数组的元素中,从而动态的创建数组,返回值就是创建的数组的长度( 创建的数组下标是从1开始的 )
(1)gsub()
[zkpk@master as]$ cat test
AAAAA
AxyzA
[zkpk@master as]$ awk '{gsub("A","a",$1);print $1}' test
aaaaa
axyza
#使用gsub()相当与全局替换( global sub )
(2)sub
[zkpk@master as]$ awk '{sub("A","a",$1);print $1}' test
aAAAA
axyzA
#使用sub只会替换指定范围内第一次匹配符合条件的字符
#注:gsub()还支持一些正则表达式( 值都要用双引号( " " )括起来,不可以用单引号(' ') )
[zkpk@master as]$ cat test2
aBcDeFg
ABDGQ??
[zkpk@master as]$ awk '{gsub("[A-Z]","+",$1);print $1}' test2
a+c+e+g
+++++??
(3)length()
[zkpk@master as]$ awk 'BEGIN{str="I LOVE YOU" ; print length(str)}'
10
[zkpk@master as]$ awk '{print $0,length()}' test
AAAAA 5
AxyzA 5
#当length()没有指定参数时,默认$0为函数参数
(4)index()
[zkpk@master as]$ awk 'BEGIN{print index("abcdefg" , "f")}'
6
#返回 f 在 abcdefg 字符串中第一次出现的位置
[zkpk@master as]$ cat test3
yaaaaa
ayaaaa
aayaya
aaayaa
[zkpk@master as]$ awk '{print $1 , index($1,"y")}' test3
yaaaaa 1
ayaaaa 2
aayaya 3
aaayaa 4
#第3行就有两个y,但是index函数只返回第一次匹配到的位置
(5)split
[zkpk@master as]$ awk -v str="what:is:your:name" 'BEGIN{print split(str,arr,":")}'
4
#将字符串str根据":"进行分割后放在arr中,命令结束返回数组arr长度,长度为4
[zkpk@master as]$ awk -v str="what:is:your:name" 'BEGIN{len = split(str,arr,":");\
> for(i=1;i<=len;i++){print i , arr[i]}} '
1 what
2 is
3 your
4 name
#分割,遍历数组
1.systime():得到时间戳,返回从1970年1月1日开始到当前时间(不计闰年)的整秒数
1.asort():对数组元素进行排序,返回值为新数组长度
2.asorti():对数组元素下标进行排序,返回值为新数组长度asorti()会根据原数组中的下标的字母顺序进行排序,并且将排序后的下标放在一个新的数组中,并且asorti函数会返回新数组的长度
(1)asort()
[zkpk@master as]$ awk 'BEGIN{arr["a"]=100;arr["b"]=99;arr["c"]=66;for(i in arr){print i,arr[i]}}'
a 100
b 99
c 66
[zkpk@master as]$ awk 'BEGIN{arr["a"]=100;arr["b"]=99;arr["c"]=66;asort(arr);\
> for(i in arr){print i,arr[i]}}'
1 66
2 99
3 100
#使用asort函数进行排序后,再次输出的数组,已经按照值的大小进行了排序,但是数组下标也被重置成了纯数字
[zkpk@master as]$ awk 'BEGIN{arr["a"]=100;arr["b"]=99;arr["c"]=66;asort(arr,new);\
for(i in arr){print i,arr[i]}}'
a 100
b 99
c 66
[zkpk@master as]$ awk 'BEGIN{arr["a"]=100;arr["b"]=99;arr["c"]=66;asort(arr,new);\
for(i in new){print i,new[i]}}'
1 66
2 99
3 100
#排序后的数组放在了new里,arr数组不变,如果写上 len=asort(arr,new) ,那么len则是asort函数返回的数组的长度
(2)asorti()
[zkpk@master as]$ awk 'BEGIN{arr["z"]=88 ; arr["o"]=99 ; arr["a"]=100 ; \
> for ( i in arr ){ print i , arr[i] }}'
z 88
a 100
o 99
[zkpk@master as]$ awk 'BEGIN{arr["z"]=88 ; arr["o"]=99 ; arr["a"]=100 ; \
> len = asorti(arr,new) ; for ( i in arr ){ print i , arr[i] }}'
z 88
a 100
o 99
[zkpk@master as]$ awk 'BEGIN{arr["z"]=88 ; arr["o"]=99 ; arr["a"]=100 ; \
> len = asorti(arr,new) ; for ( i in new ){ print i , new[i] }}'
1 a
2 o
3 z
#asorti()会根据原数组中的下标的字母顺序进行排序,并且将排序后的下标
#放在一个新的数组中,并且asorti函数会返回新数组的长度