awk--动作(action)
摘要
在awk--简述中我们讲到awk是由pattern-action组合而成的,关于pattern我们已经awk--模式(pattern)在讲述,接下来就来看下awk的action.
动作是什么
我们同样举在awk--模式(pattern)我们所使用的例子:
//打印当前目录下面,大小大于1K(1024)的文件或目录名称 ls -l | awk 'NR > 1 && $5 > 1024 {print $9}'
加粗字体部分即为动作,本处的动作是打印被awk分割后的第9个字段,
所以可以认为在awk 中执行主要是由模式匹配先匹配,匹配正确后再进行执行即action(动作).
有什么动作
awk的action主要包含两类:
1.常规的表达式(包含设置常量,变量,赋值, 函数调用(包含 print,printf 和 自定义函数))
2.流程(比如if while for等)
动作的使用
常量和变量
- awk的常量/变量类型
awk只有两种数据类型:
1)字符串
2)数值
相互转换
字符串指的是被双引号(“”)包裹的串,字符串和数值可以按照情况自己转换.
需要强行转换的时候可以使用:
1)字符串->数值: string+0(因为0对数组没有影响- -),比如 “1” + 0
2)数值->字符串: number””即可,比如 1””;
支持运算
1)对于字符串只有一种运算符:拼接
比如: awk -F "\|\|" '{print $1":"$2}' worker.txt 输出: 1:Jack 2:Lip
2)对于数值而言:awk支持的常规C的运算符操作,如:++,--..等
- 内建变量
(1)ARGC/ARGV
含义:输入行参数的个数/数组
例子:略
(2)FILENAME
含义:当前输入行的文件名称
例子:在存在多个文件时,可以进行对不同的输入行进行区分,
awk '{ if(FILENAME=="worker.txt") { print 1 } else{ print 2} }' worker.txt countries
(3)NR
含义:当前输入行的序号(不断累加)
例子:
awk '{printf("%s ",NR)} END{printf("\n")}' worker.txt countries 输出:1 2 3 4 5 6 7 8 9 10 11 12 13
(4)FNR
含义:当前输入行对输入内容的序号(每个输入都会被独立的排列序号)
例子:
awk '{printf("%s ",FNR)} END{printf("\n")}' worker.txt countries 输出:1 2 1 2 3 4 5 6 7 8 9 10 11
注意:存在多个文件时,NR是不断累加的,因此对于仅有两个输入的时候,只需要根据NR,FNR就可以区别输入源了,
比如(2)FILENAME的例子就可以修改为:
awk 'NR==FNR{ print 1} NR > FNR{ print 2 }' worker.txt country
(5)NF
含义:当前输入行的字段总数(可以理解为:ALineInput.splite(`FS`).count())
例子:
awk -F "\|\|" '{printf("当前输入行的字段数:%d,输入行:%s\n",NF,$0)}' worker.txt 输出: 当前输入行的字段数:5,输入行:1||Jack||25||man||Chinese,FuJian
(6)FS/OFS
含义:设置输入/输出的分隔符
例子:
awk 'BEGIN{FS="\|\|";printf("%3s\n","ID")} {printf("%.3d\n",$1)}' worker.txt 或 awk –F "\|\|" '{printf("%3s\n","ID")} {printf("%.3d\n",$1)}' worker.txt
(7)RS/ORS
含义:输入/输出记录的分隔符
例子:略,默认RS=ORS= "\n "
(8)OFMT
含义:数值的输出格式
例子:略
(9)RLENGTH/RSTART
含义:awk内置函数match(s,r)(匹配函数)的返回值,其中RLENGTH(rightlength)表示匹配长度,RSTART表示匹配r的输入行s的起始位置
例子:
//匹配输入行是否存在字符串"Chinese" awk -F "\|\|" '{match($0,"Chinese");if(RLENGTH > 1) printf("\"Chinese\" can match in \"%s\",RSTART:%d,RLENGTH:%d\n",$0,RSTART,RLENGTH)}' worker.txt 输出: "Chinese" can match in "1||Jack||25||man||Chinese,FuJian",RSTART:19,RLENGTH:7 可见RSTART的计数是由1开始的
(10)SUBSEP
含义:下标分割符
例子:略,默认SUBSET="\34"
- 自定义变量
用户子自定义的变量的首字母不能是数字,需要在命令行前面设置变量可以使用awk –v key=value的方式,
比如:
//设置在动作中使用的自定义变量current_path awk -v current_path = `pwd` '{print current_path}' input
也可以采用如下方式:
var="var!" awk 'BEGIN{print "'$var'"}' empty.txt 输出为: var!
不过貌似变量var的值不能包含空格(环境:Ubuntu14.04,bash).
数组
- 简介
awk提供了一维数组,用于存放字符串与数值.数组和数组元素都不需要事先声明,也不需要说明数组中有多少个元素.
就像变量一样,当被提及时数组元素就会被创建,数组元素的默认初始值为0或者空字符串””.
awk的下标采用字符串,因此也被成为关联数组(类似与hash或map).
比如:
awk '/Asia/ {pop["Asia"] += $3} END {print "Asia populate:", pop["Asia"]}' countries 输出: Asia populate: 2173
- 遍历数组
在awk中遍历数组可以有两种方式
方式1) for(i=0; i< **; i++){}
方式2) for(val in array) {} (即foreach)
方式1的缺陷是我们需要知道当前数组的个数,因此在awk中更适用于方式2.
注意:如果需要判断元素是否存在在数组话,可以采用下面的方法:
if(val in array)
比如:
if (“Asia” in pop)
如果采用if (array[variable] != **) 且**为空的那么该if返回true,
原因是在进行array[variable]的时候,如果awk判断下标为variable的元素不存在时就会默认创建一个空字符串.
- 删除数组
1)删除单个 delete array[val]即可
2)全部删除 for(val in array) delete array[val]
- 多维数组
实际上awk并不提供多维数组,但是因为awk的下标采用的字符串,所以你可以按照你的方式构造多维数组,比如:
awk '{for(i = 1; i <=3; i++) {for(j=1;j<=3;j++) {pop[i""j]=i*j}}} END{for (data in pop) {print "sub=",data,"data:",pop[data]}}' worker.txt 输出为: sub= 11 data: 1 sub= 12 data: 2 sub= 13 data: 3 sub= 21 data: 2 sub= 22 data: 4 sub= 23 data: 6 sub= 31 data: 3 sub= 32 data: 6 sub= 33 data: 9
其中pop[i""j]是为了将数值转化为字符串
函数
- 内建函数
1)数值型内建函数
awk内建算数函数类似于C语言包括:
sin(x)
cos(x)
atan2(y,x)
rand()
srand(x)
exp(x)
log(x)
sqrt(x)
int(x)
比如:
awk 'BEGIN{print "sqrt(4) =",sqrt(4)}' empty.txt 输出: sqrt(4) = 2
2)字符型内建函数
awk中字符串的内置函数和C的存在略微不同,主要有以下几个:
(1)sub(r,s) 含义:将$0的最左最长的r,替换成s,返回替换的次数
sub(r,s,t) 含义:将t的最左最长的r,替换成s,返回替换的次数
比如:
awk -F "\|\|" '{sub("man","female",$0);print}' worker.txt 输出为: 1||Jack||25||female||Chinese,FuJian 2||Lip||30||female||American
(2)gsub(r,s) 含义:将输入行中的字符串r替换为s,返回替换发生的次数
gsub(r,s,t)含义: t为输入行,将t中出现的字符串r替换替换成s
比如:
awk '{gsub("man","female"); print $0}' worker.txt 输出为: 1||Jack||25||female||Chinese,FuJian 2||Lip||30||female||American awk '{gsub("man","female",$0); print $0}' worker.txt 输出为: 1||Jack||25||female||Chinese,FuJian 2||Lip||30||female||American
另外:
对于一个在sub或gsub执行的替换来说,字符&在s中的任意一次出现都会被替换成r.(即& = r)
比如:
awk '{gsub(/a/,"\"&*&\"",$0); print}' worker.txt 输出为: 1||J"a*a"ck||25||m"a*a"n||Chinese,FuJi"a*a"n 2||Lip||30||m"a*a"n||Americ"a*a"n 即:使用“a*a”替换原本的a
(3)index(s,t)
含义:返回字符串t在s中第一次出现的位置,如果未找到返回0
比如:
awk '{print "\"man\" index:" index($0,"man")}' worker.txt 输出为: "man" index:14 "man" index:13
(4)length(s)
含义:返回字符串s的长度
比如:
awk -F "\|\|" '{printf("\"%s\" length:%d\n",$2,length($2))}' worker.txt 输出为: "Jack" length:4 "Lip" length:3
(5)match(s,r)
含义:测试s中是否包含能被r匹配的字符串,返回s中被匹配的起始位置,并设置内置变量RSTART,RLENGTH
比如:
awk -F "\|\|" '{match($0,"Chinese");if(RLENGTH > 1) printf("\"Chinese\" can match in \"%s\",RSTART:%d,RLENGTH:%d\n",$0,RSTART,RLENGTH)}' worker.txt 输出为: "Chinese" can match in "1||Jack||25||man||Chinese,FuJian",RSTART:19,RLENGTH:7 可见RSTART的计数是由1开始的
(6)split(s,a) 含义:用FS将s分割到数组a中,返回字段的个数
split(s,a,fs) 含义:用fs将s分割到数组a中,返回字段的个数
比如:
awk 'BEGIN { FS="\|\|"} {iAddress = split($5,Address,","); for(i=1;i<=iAddress;i++){ printf("currunt %d Address:%s\n",i,Address[i])} }' worker.txt 输出为: currunt 1 Address:Chinese currunt 2 Address:FuJian currunt 1 Address:American 可见在split中数组存储的顺序为arr[“1”],arr[“2”]....
(7)sprintf(fmt,expr-list)
含义: 按照格式格式化字符串,返回生成的格式化字符串
比如:
awk -F "\|\|" '{print sprintf("id:%d name:%s",$1,$2)}' worker.txt 输出为: id:1 name:Jack id:2 name:Lip
-
自定义函数
awk中使用自定义函数可以使用function关键字
比如:
awk 'BEGIN{print max(1,2) } function max(m,n) { return m > n ? m : n}' empty.txt 输出:2 awk 'BEGIN{ myprint("hello,world") } function max(m,n) { return m > n ? m : n} function myprint(x){print x}' empty.txt 输出: hello,world
流程控制
awk中的流程控制语句用法同C语言类似
1)if..else..
比如:
//区分多个输入的方法(缺点需要知道输入的文件名称) awk -F "\|\|| " '{if(FILENAME == "worker.txt") {print "input is worker.txt",$2} else if(FILENAME == "countries") { print "input is countries",$4}}' worker.txt countries
2)for/while/break
略
输出
1)输出到stdout(默认)
比如:
awk -F "\|\|" '{print sprintf("id:%d name:%s",$1,$2)}' worker.txt 输出: id:1 name:Jack id:2 name:Lip
2)输出到file
比如: awk -F "\|\|" '{print sprintf("id:%d name:%s",$1,$2) > "test.txt"}' worker.txt
cat test.txt内容为: id:1 name:Jack id:2 name:Lip
注意:
在同一个程序中如果存在输出到文件中(如上面的:test.txt),在稍后的流程又需要读取这个文件(例子:test.txt)的话,那么你必须在读取之前先关闭一下文件(将缓存的内容写入到文件中),在读取文件.
关闭的写法为:
close(“test.txt”);
3)输出到管道
比如:
awk -F "\|\|" '{print "/" |"df" }' worker.txt 输出: 文件系统 1K-块 已用 可用 已用% 挂载点 udev 1946180 4 1946176 1% /dev tmpfs 391988 1136 390852 1% /run /dev/dm-0 611014912 16737160 563216968 3% / none 4 0 4 0% /sys/fs/cgroup none 5120 0 5120 0% /run/lock none 1959932 152 1959780 1% /run/shm none 102400 72 102328 1% /run/user /dev/sda1 240972 110988 117543 49% /boot
输入
1)从file
比如: awk '{print }' worker.txt 输出: 1||Jack||25||man||Chinese,FuJian 2||Lip||30||man||American
2)从stdin
比如:
awk '{print }' //回显 输入/输出: Hello,World Hello,World
3)从pipe
比如:
cat worker.txt | awk '{print}' 输出: 1||Jack||25||man||Chinese,FuJian 2||Lip||30||man||American
4)从getline
名如其意,getline存在6中方式的调用:
表达式 | 被设置的变量 |
getline | $0, NF, NR, FNR |
getline var | var, NR, FNR |
getline <file | $0, NF |
getline var <file | var |
cmd | getline | $0, NF |
cmd | getline var | var |
注意:
使用getline的返回值有3中情况1(存在记录),0(文件末尾),-1(打开失败或不存在)
对于awk中的判断语句(if,while,for),0=false,非0=true
所以如果你采用;
awk 'END{while(getline < "no_exist_file") {print $0}}' empty.txt
将进入无限的循环,所以正确的写法是:
awk 'END{while(getline < "no_exist_file" > 0) {print $0}}' empty.txt
下面来看下getline的具体用法:
1)getline
awk '{while(getline > 0) {print $0}}' worker.txt 输出为: 2||Lip||30||man||American
2)getline var
awk '{while(getline var > 0) {print var}}' worker.txt 输出为: 2||Lip||30||man||American 为什么1,2仅有一行输出呢? getline函数的功能在于抓取下一个记录.
3)getline < file
awk 'END{while(getline < "worker.txt" > 0) print $0,"NF=",NF}' empty.txt 输出为: 1||Jack||25||man||Chinese,FuJian NF= 1 2||Lip||30||man||American NF= 1
4)getline var < file
awk 'END{while(getline var< "worker.txt" > 0) print var}' empty.txt 输出为: awk 'END{while(getline var< "worker.txt" > 0) print var}' empty.txt
5)cmd | getline
awk '{while("who" | getline > 0) print "NF(Current Line Splite Count):",NF,"data:",$0}' worker.txt 输出为: NF(Current Line Splite Count): 5 data: lin :0 2016-11-19 16:55 (:0) NF(Current Line Splite Count): 5 data: lin pts/2 2016-11-19 19:57 (:0) NF(Current Line Splite Count): 5 data: lin pts/13 2016-11-19 20:49 (:0) NF(Current Line Splite Count): 5 data: lin pts/25 2016-11-19 21:16 (:0)
6)cmd | get line var
awk '{while("who" | getline line > 0) print line}' worker.txt 输出为: lin :0 2016-11-19 16:55 (:0) lin pts/2 2016-11-19 19:57 (:0) lin pts/13 2016-11-19 20:49 (:0) lin pts/25 2016-11-19 21:16 (:0)
可以看出:
直接采用awk的输入(文件/管道/标准输入)的情况使用getline,将根据实际情况设置$0(var),NR,FNR.
凡事将getline重定向给var,那么NF将不会被设置(相反同义,凡是$0被设置后,NF就会被设置)
与其他程序的交互
1)使用pipe
awk 'END{while("who" | getline line > 0) print line}' empty.txt 输出为: lin :0 2016-11-20 08:57 (:0) lin pts/0 2016-11-20 09:02 (:0)
2)使用system
awk 'END{while(system("ls -lh") | getline line > 0) print line}' empty.txt 输出为: 总用量 20K -rw-rw-r-- 1 lin lin 256 11月 6 17:05 countries -rw-rw-r-- 1 lin lin 0 11月 19 23:09 empty.txt -rw-rw-r-- 1 lin lin 35 11月 6 16:53 ip.txt -rw-rw-r-- 1 lin lin 13 10月 28 22:51 print.sh -rw-rw-r-- 1 lin lin 29 11月 15 22:40 test.txt -rw-rw-r-- 1 lin lin 59 11月 9 22:24 worker.txt
总结
本节主要讲述了awk 动作,动作主要的作用是用于对被模式匹配后的数据进行操作。
参考
- https://github.com/wuzhouhui/awk (The AWK Programming Language 中文翻译版)