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("
")}' worker.txt countries
输出:1  2  3  4  5  6  7  8  9  10  11  12  13

(4)FNR

含义:当前输入行对输入内容的序号(每个输入都会被独立的排列序号)

例子:

awk '{printf("%s  ",FNR)} END{printf("
")}' 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
",NF,$0)}' worker.txt
输出:
当前输入行的字段数:5,输入行:1||Jack||25||man||Chinese,FuJian 

(6)FS/OFS

含义:设置输入/输出的分隔符

例子:

awk 'BEGIN{FS="||";printf("%3s
","ID")} {printf("%.3d
",$1)}'  worker.txt
或
awk –F  "||"  '{printf("%3s
","ID")} {printf("%.3d
",$1)}'  worker.txt

(7)RS/ORS

含义:输入/输出记录的分隔符

例子:略,默认RS=ORS= " "

(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
",$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
",$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
",$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
",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://www.cnblogs.com/hejianglin/p/6082755.html