1.6 awk关系、布尔运算符、表达式
AWK的大部分运算符及其意义与C/C++、Java、Linux等相似,如:
< 小于
> 大于
<= 小于等于
>= 大于等于
== 等于
!= 不等于
|| 逻辑或
&& 逻辑与
! 逻辑非
+ 加
- 减
* 乘
/ 除
% 模
^或** 乘方
++x 在返回x值之前,x变量加1
x++ 在返回x值之后,x变量加1
AWK独有的运算符有:~ 匹配正则表达式、!~ 不匹配正则表达式。
awk 'BEGIN {FS=":"} $1~/root/' /etc/passwd ~正则表达式为匹配字符串
awk 'BEGIN {FS=":"} {if($3==10||$4==10) print $0}' /etc/passwd ==是为数字匹配
awk 'BEGIN {FS=":"} {if($3~10||$4~10) print $0}' /etc/passwd 正则表达式~表示包含
awk执行文件:
#!/bin/awk -f
BEGIN {FS=","}
{ total=$4+$5+$6+$7+$8
avg=total/5
print $1,avg
}
与其他编程语言一样,awk表达式用于存储、操作和获取数据,一个awk表达式可由数值、字符常量、变量、操作符、函数和正则表达式自由组合而成。
变量是一个值的标识符,定义awk变量非常方便,只需定义一个变量名并将值赋给它即可。变量名只能包含字母、数字和下划线,而且不能以数字开头,这与C/C++、Java的变量定义规范相同。
1.7 awk格式化输出:
awk的一大主要功能是产生报表,报表就要求按照预定的格式输出,awk借鉴C语言的语法,定义了printf输出语句,它可以规定输出的格式。
printf:printf (格式控制符,参数)
awk格式控制符及其意义如下:
- 左对齐
width 域的步长
.prec 小数点右边的位数
%c ASCII字符
%d 整型数
%e 浮点数,科学记数法
%f 浮点数
%o 八进制数
%s 字符串
%x 十六进制数
awk 'BEGIN {FS=","; print "NAME\t\tPHCHNUMBER"} {printf("%-15s\t%s\n",$1,$3)}' sturecord2
结果为:
NAME PHCHNUMBER
Li Hao 80283058
Zhang Ju 2343255
Wang Bin 2342353
Zhu Lin 23434
1.8 awk内置字符串函数
awk内置字符串函数极为强大,是Shell处理字符串的常用工具。常用的字符串函数如下:
sub(regular expression,substitution string);
sub(regular expression,substitution string,target string);
gsub(r,s) 在输入文件中用s替换r
gsub(r,s,t) 在t中用s替换r
index(s,t) 返回s中字符串第一个t的位置
length(s) 返回s的长度
match(s,t) 测试s是否包含匹配t的字符串
split(r,s,t) 在t上将r分成序列s
sub(r,s,t) 将t中第1次出现的r替换为s
substr(r,s) 返回字符串r中从s开始的后缀部分
substr(r,s,t) 返回字符串r中从s开始长度为t的后缀部分
toupper(string)、tolower(string)以上两个函数分别返回参数字符串的大写和小写的形式。
variable = sprintf("string with format specifiers ",expr1,expr2,...)
该函数和printf的差别等同于C语言中printf和sprintf的差别。前者将格式化后的结果输出到输出流,而后者输出到函数的返回值中。
systime()该函数返回当前时间距离1970年1月1日之间相差的秒数。
strftime()时间格式化函数,其格式化规则等同于C语言中的strftime函数提供的规则:
数据格式含义
%a Abbreviated weekday name
%A Full weekday name
%b Abbreviated month name
%B Full month name
%c Date and time representation appropriate for locale
%d Day of month as decimal number (01 – 31)
%H Hour in 24-hour format (00 – 23)
%I Hour in 12-hour format (01 – 12)
%j Day of year as decimal number (001 – 366)
%m Month as decimal number (01 – 12)
%M Minute as decimal number (00 – 59)
%p Current locale's A.M./P.M. indicator for 12-hour clock
%S Second as decimal number (00 – 59)
%U Week of year as decimal number, with Sunday as first day of week (00 – 53)
%w Weekday as decimal number (0 – 6; Sunday is 0)
%W Week of year as decimal number, with Monday as first day of week (00 – 53)
%x Date representation for current locale
%X Time representation for current locale
%y Year without century, as decimal number (00 – 99)
%Y Year with century, as decimal number
/> awk 'BEGIN{ print strftime("%D",systime())}'
11/15/11
/> awk 'BEGIN{ now = strftime("%T"); print now}'
23:17:29
常用的内置数学函数如下:
atan2(x,y) y,x范围内的余切
cos(x) 余弦函数
exp(x) 求幂
int(x) 取整
log(x) 自然对数
sin(x) 正弦函数
sqrt(x) 平方根
/> awk 'BEGIN{print 31/3}'
10.3333
/> awk 'BEGIN{print int(31/3)}'
10
AWK还支持用户自定义函数。用户自定义函数可以放在awk脚本的任何可以放置模板和动作的地方。
方法如下:
function methodName(parameter1,parameter2,...) {
//statements
//return expression
}
给函数中本地变量传递值只使用变量的拷贝。数组通过地址或者指针传递,所以可以在函数内部直接改变数组元素的值。
函数内部使用的任何没有作为参数传递的变量都被看做是全局变量,也就是这些变量对于整个程序都是可见的。如果变量在函数中发生了变化,那么就是在整个程序中发生了改变。
唯一向函数提供本地变量的办法就是把他们放在参数列表中,这些参数通常被放在列表的最后。如果函数调用没有提供正式的参数,那么参数就初始化为空。return语句通常就返回程序控制并向调用者返回一个值。
/> cat grades
20 10
30 20
40 30
/> cat add.sc
function add(first,second) {
return first + second
}
{ print add($1,$2) }
/> awk -f add.sc grades
30
50
70
1.9 awk条件语句和循环语句
awk条件语句和循环语句与C语言的语法完全一样.
if (条件表达式)
动作1
[else
动作2]
while (条件表达式)
动作
do
动作
while (条件表达式)
for (设置计数器初值;测试计数器;计数器变化)
动作
awk中还提供了一种special for的循环,见如下声明:
for (item in arrayname) {
print arrayname[item]
}
1.10 awk数组
数组是用于存储一系列值的变量,这些值之间通常是有联系的,可通过索引来访问数组的值,索引需要用中括号括起,数组的基本格式为:
array[index]=value
关联数组是指数组的索引可以是字符串,也可以是数字。关联数组在索引和数组元素值之间建立起关联,对每一个数组元素,awk自动维护了一对值:索引和数组元素值。关联数组的值无需以连续的地址进行存储,awk的所有数组都是关联数组。
字符串和数字之间的差别是明显的,如,我们使用array[09]指定一个数组值,如果换成array[9]就不能指定到与array[09]相同的值。
由于awk中数组的下标可以是数字和字母,数组的下标通常被称为关键字(key)。值和关键字都存储在内部的一张针对key/value应用hash的表格里。由于hash不是顺序存储。
数组和变量一样,都是在使用时自动创建的,awk也同样会自动判断其存储的是数字还是字符串。一般而言,awk中的数组用来从记录中收集信息,可以用于计算总和、统计单词以及跟踪模板被匹配的次数等等。
/> cat employees
Tom Jones 4424 5/12/66 543354
Mary Adams 5346 11/4/63 28765
Sally Chang 1654 7/22/54 650000
Billy Black 1683 9/23/44 336500
/> awk '{name[x++] = $2}; END{for (i = 0; i < NR; i++) print i, name[i]}' employees
0 Jones
1 Adams
2 Chang
3 Black
在上例中,数组name的下标是变量x。awk初始化该变量的值为0,在每次使用后自增1,读取文件中的第二个域的值被依次赋值给name数组的各个元素。在END模块中,for循环遍历数组的值。因为下标是关键字,所以它不一定从0开始,可以从任何值开始。
#这里是用内置变量NR作为数组的下标了。
/> awk '{id[NR] = $3}; END {for (x = 1; x <= NR; x++) print id[x]}' employees
4424
5346
1654
1683
再如: /> cat db
Tom Jones
Mary Adams
Sally Chang
Billy Black
Tom Savage
Tom Chung
Reggie Steel
Tommy Tucker
/> awk '/^Tom/{name[NR]=$1}; END {for(i = 1;i <= NR; i++) print name[i]}' db
Tom
Tom
Tom
Tommy
从输出结果可以看出,只有匹配正则表达式的记录的第一个域被赋值给数组name的指定下标元素。因为用NR作为下标,所以数组的下标不可能是连续的,因此在END模块中用传统的for循环打印时,不存在的元素就打印空字符串了。下面我们看看用special for的方式会有什么样的输出。
/> awk '/^Tom/{name[NR]=$1};END{for(i in name) print name[i]}' db
Tom
Tom
Tommy
Tom
下面我们看一下用字符串作为下标的例子:(如果下标是字符串文字常量,则需要用双引号括起来)
/> cat testfile2
tom
mary
sean
tom
mary
mary
bob
mary
alex
/> awk '/tom/{count["tom"]++}; /mary/{count["mary"]++}; END{print "There are " count["tom"] \
" Toms and " count["mary"] " Marys in the file."} testfile2
There are 2 Toms and 4 Marys in the file.
在上例中,count数组有两个元素,下标分别为tom和mary,每一个元素的初始值都是0,没有tom被匹配的时候,count["tom"]就会加一,count["mary"]在匹配mary的时候也同样如此。END模块中打印出存储在数组中的各个元素。
/> awk '{count[$1]++}; END{for(name in count) printf "%-5s%d\n",name, count[name]}' testfile2
mary 4
tom 2
alex 1
bob 1
sean 1
在上例中,awk是以记录的域作为数组count的下标。
/> awk '{count[$1]++; if (count[$1] > 1) name[$1]++}; END{print "The duplicates were "; for(i in name) print i}' testfile2
The duplicates were
mary
tom
在上例中,如count[$1]的元素值大于1的时候,也就是当名字出现多次的时候,一个新的数组name将被初始化,最后打印出那么数组中重复出现的名字下标。
之前我们介绍的都是如何给数组添加新的元素,并赋予初值,现在我们需要介绍一下如何删除数组中已经存在的元素。要完成这一功能我们需要使用内置函数delete,见如下命令:
/> awk '{count[$1]++}; \
END{for(name in count) {\
if (count[name] == 1)\
delete count[name];\
} \
for (name in count) \
print name}' testfile2
mary
tom
上例中的主要技巧来自END模块,先是变量count数组,如果数组中某个元素的值等于1,则删除该元素,这样等同于删除只出现一次的名字。最后用special for循环打印出数组中仍然存在的元素下标名称。
最后我们来看一下如何使用命令行参数数组,见如下命令:
/> awk 'BEGIN {for(i = 0; i < ARGC; i++) printf("argv[%d] is %s.\n",i,ARGV[i]); printf("The number of arguments, ARGC=%d\n",ARGC)}' testfile "Peter Pan" 12
argv[0] is awk.
argv[1] is testfile.
argv[2] is Peter Pan.
argv[3] is 12.
The number of arguments, ARGC=4
从输出结果可以看出,命令行参数数组ARGV是以0作为起始下标的,命令行的第一个参数为命令本身(awk),这个使用方式和C语句main函数完全一致。
/> awk 'BEGIN{name=ARGV[2]; print "ARGV[2] is " ARGV[2]}; $1 ~ name{print $0}' testfile2 "bob"
ARGV[2] is bob
bob
awk: (FILENAME=testfile2 FNR=9) fatal: cannot open file `bob' for reading (No such file or directory)
先解释一下以上命令的含义,name变量被赋值为命令行的第三个参数,即bob,之后再在输入文件中找到匹配该变量值的记录,并打印出该记录。
在输出的第二行报出了awk的处理错误信息,这主要是因为awk将bob视为输入文件来处理了,然而事实上这个文件并不存在,下面我们需要做进一步的处理来修正这个问题。
/> awk 'BEGIN{name=ARGV[2]; print "ARGV[2] is " ARGV[2]; delete ARGV[2]}; $1 ~ name{print $0}' testfile2 "bob"
ARGV[2] is bob
bob
从输出结果中我们可以看到我们得到了我们想要的结果。需要注意的是delete函数的调用必要要在BEGIN模块中完成,因为这时awk还没有开始读取命令行参数中指定的文件。