AWK编程总结(2)

1.6 awk关系、布尔运算符、表达式

AWK的大部分运算符及其意义与C/C++JavaLinux等相似,如:

< 小于

> 大于

<= 小于等于

>= 大于等于

== 等于

!= 不等于

|| 逻辑或

&& 逻辑与

! 逻辑非

+

-

*

/

%

^** 乘方

++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输出语句,它可以规定输出的格式。

printfprintf (格式控制符,参数)

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语言中printfsprintf的差别。前者将格式化后的结果输出到输出流,而后者输出到函数的返回值中。

systime()该函数返回当前时间距离197011日之间相差的秒数。

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的下标是变量xawk初始化该变量的值为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数组有两个元素,下标分别为tommary,每一个元素的初始值都是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的处理错误信息,这主要是因为awkbob视为输入文件来处理了,然而事实上这个文件并不存在,下面我们需要做进一步的处理来修正这个问题。

    /> 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还没有开始读取命令行参数中指定的文件。

 

猜你喜欢

转载自aoyouzi.iteye.com/blog/1864497