awk manual-2
上一篇 / 下一篇 2008-06-23 13:55:13 / 个人分类:Shell编程
5.awk中数组
awk程序中允许使用字符串当做数组的下标(index).利用这个特色十分有助于资料统计工作.(使用字符串当下标的数组称为Associative Array)
首先建立一个数据文件,并取名为reg.dat.此为一学生注册的资料文件;第一栏为学生姓名,其后为该生所修课程.
Mary O.S. Arch. Discrete
Steve D.S. Algorithm Arch.
Wang Discrete Graphics O.S.
Lisa Graphics A.I.
Lily Discrete Algorithm
awk中数组的特性
使用字符串当数组的下标(index).
使用数组前不须宣告数组名及其大小.
例如:希望用数组来记录reg.dat中各门课程的修课人数.
这情况,有二项信息必须储存:
(a)课程名称,如: "O.S.","Arch.".. ,共有哪些课程事先并不明确.
(b)各课程的修课人数.如:有几个人修"O.S."
在awk中只要用一个数组就可同时记录上列信息.其方法如下:
使用一个数组Number[ ] :
以课程名称当Number[ ]的下标.
以Number[ ]中不同下标所对映的元素代表修课人数.
例如:
有2个学生修"O.S.",则以Number["O.S."] = 2表之.
若修"O.S."的人数增加一人,则Number["O.S."] = Number["O.S."] + 1或Number["O.S."]++ .
如何取出数组中储存的信息
以C语言为例,声明int Arr[100];之后,若想得知Arr[ ]中所储存的数据,只须用一个循环,如:
for(i=0; i<100; i++) printf("%d\n", Arr[i]);
即可.上式中:
数组Arr[ ]的下标: 0, 1, 2,..., 99
数组Arr[ ]中各下标所对应的值: Arr[0], Arr[1],...Arr[99]
但awk中使用数组并不须事先宣告.以刚才使用的Number[ ]而言,程序执行前,并不知将来有哪些课程名称可能被当成Number[ ]的下标.
awk提供了一个指令,藉由该指令awk会自动找寻数组中使用过的所有下标.以Number[ ]为例, awk将会找到"O.S.", "Arch.",...
使用该指令时,须指定所要找寻的数组,及一个变量. awk会使用该的变量来记录从数组中找到的每一个下标.例如
for(course in Number){....}
指定用course来记录awk从Number[ ]中所找到的下标. awk每找到一个下标时,就用course记录该下标之值且执行{....}中之指令.藉由这个方式便可取出数组中储存的信息.
(详见下例)
[范例: ]统计各科修课人数,并印出结果.
建立如下程序,并取名为course.awk:
{ for( i=2; i <= NF; i++) Number[$i]++ }
END{for(course in Number) printf("%10s %d\n", course, Number[course] )}
执行下列命令:
$awk -f course.awk reg.dat
执行结果如下:
Graphics 2
O.S. 2
Discrete 3
A.I. 1
D.S. 1
Arch. 2
Algorithm 2
[说明: ]
这程序包含二个Pattern { Actions }指令.
{ for( i=2; i <= NF; i++) Number[$i]++ }
END{for(course in Number) printf("%10s %d\n", course, Number[course] )}
第一个Pattern { Actions }指令中省略了Pattern部分.故随着
每笔数据行的读入其Actions部分将逐次无条件被执行.
以awk读入第一笔资料" Mary O.S. Arch. Discrete"为例,因为该笔数据NF = 4(有4个字段),故该Action的for Loop中i = 2,3,4.
i $i最初Number[$i] Number[$i]++之后
i=2时$i="O.S." Number["O.S."]的值从默认的0,变成了1 ;
i=3时$i="Arch." Number["Arch."]的值从默认的0,变成了1 ;
同理,i=4时$i="Discrete" Number["Discrete"]的值从默认的0,变成了1 ;
第二个Pattern { Actions }指令中END为awk之保留字,为Pattern的一种.
END成立(其值为true)的条件是: "awk处理完所有数据,即将离开程序时. "
平常读入数据行时, END并不成立,故其后的Actions并不被执行;
唯有当awk读完所有数据时,该Actions才会被执行(注意,不管数据行有多少笔, END仅在最后才成立,故该Actions仅被执行一次.)
BEGIN与END有点类似,是awk中另一个保留的Pattern.
唯一不同的是: "以BEGIN为Pattern的Actions于程序一开始执行时,被执行一次."
NF为awk的内建变量,用以表示awk正处理的数据行中,所包含的字段个数.
awk程序中若含有以$开头的自定变量,都将以如下方式解释:
以i= 2为例, $i = $2表第二个字段数据. (实际上, $在awk中为一运算符(Operator),用以取得字段数据.)
6.awk程序中使用Shell命令
awk程序中允许呼叫Shell指令.并提供管道解决awk与系统间数据传递的问题.所以awk很容易使用系统资源.读者可利用这个特点来编写某些适用的系统工具.
[范例: ]写一个awk程序来打印出线上人数.
将下列程序建文件,命名为count.awk
BEGIN {
while ( "who" | getline ) n++
print n
}
并执行下列命令:
awk -f count.awk
执行结果将会印出目前在线人数
[说明: ]
awk程序并不一定要处理数据文件.以本例而言,仅输入程序文件count.awk,未输入任何数据文件.
BEGIN和END同为awk中的一种Pattern.以BEGIN为Pattern的Actions ,只有在awk开始执行程序,尚未开启任何输入文件前,被执行一次.(注意:只被执行一次)
"|"为awk中表示管道的符号. awk把|之前的字符串"who"当成Shell上的命令,并将该命令送往Shell执行,执行的结果(原先应于屏幕印出者)则藉由pipe送进awk程序中.
getline为awk所提供的输入指令.
其语法如下:
语法 | 由何处读取数据 | 数据读入后置于 |
getline var < file | 所指定的file | 变量var(var省略时,表示置于$0) |
getline var | pipe变量 | 变量var(var省略时,表示置于$0) |
getline var | 见注一 | 变量var(var省略时,表示置于$0) |
注一:当Pattern为BEGIN或END时, getline将由stdin读取数据,否则由awk正处理的数据文件上读取数据.
getline一次读取一行数据,若读取成功则return 1,若读取失败则return -1,若遇到文件结束(EOF),则return 0;
本程序使用getline所return的数据来做为while判断循环停止的条件,某些awk版本较旧,并不容许使用者改变$0之值.这种版的awk执行本程序时会产生Error,读者可于getline之后置上一个变量(如此, getline读进来的数据便不会被置于$0 ),或直接改用gawk便可解决.
7.awk程序的应用实例
本节将示范一个统计上班到达时间及迟到次数的程序.
这程序每日被执行时将读入二个文件:
员工当日到班时间的数据文件(如下列之arr.dat )
存放员工当月迟到累计次数的文件.
当程序执行执完毕后将更新第二个文件的数据(迟到次数),并打印当日的报表.这程序将分成下列数小节逐步完成,其大纲如下:
[7.1]在到班资料文件arr.dat之前增加一行抬头
"ID Number Arrvial Time",并产生报表输出到文件today_rpt1中.
<思考:在awk中如何将数据输出到文件>
[7.2]将today_rpt1上的数据按员工代号排序,并加注执行当日日期;产生文件today_rpt2
<思考awk中如何运用系统资源及awk中Pipe之特性>
[7.3]将awk程序包含在一个shell scrīpt文件中
[7.4]于today_rpt2每日报表上,迟到者之前加上"*",并加注当日平均到班时间;
产生文件today_rpt3
[7.5]从文件中读取当月迟到次数,并根据当日出勤状况更新迟到累计数.
<思考使用者在awk中如何读取文件数据>
某公司其员工到勤时间档如下,取名为arr.dat.文件中第一栏为员工代号,第二栏为到达时间.本范例中,将使用该文件为数据文件.
1034 7:26
1025 7:27
1101 7:32
1006 7:45
1012 7:46
1028 7:49
1051 7:51
1029 7:57
1042 7:59
1008 8:01
1052 8:05
1005 8:12
Ø重定向输出到文件
awk中并未提供如C语言中之fopen()指令,也未有fprintf()文件输出这样的指令.但awk中任何输出函数之后皆可借助使用与UNIX中类似的I/O重定向符,将输出的数据重定向到指定的文件;其符号仍为> (输出到一个新产生的文件)或>> (添加输出的数据到文件末尾).
[例:]在到班数据文件arr.dat之前增加一行抬头如下:
"ID Number Arrival Time",并产生报表输出到文件today_rpt1中
建立如下文件并取名为reformat1.awk
BEGIN { print " ID Number Arrival Time" > "today_rpt1"
print "===========================" > "today_rpt1"
}
{ printf(" %s %s\n", $1,$2 ) > "today_rpt1" }
执行:
$awk -f reformat1.awk arr.dat
执行后将产生文件today_rpt1,其内容如下:
ID Number Arrival Time
============================
1034 7:26
1025 7:27
1101 7:32
1006 7:45
1012 7:46
1028 7:49
1051 7:51
1029 7:57
1042 7:59
1008 8:01
1052 8:05
1005 8:12
[说明: ]
awk程序中,文件名称today_rpt1的前后须以" (双引号)括住,表示today_rpt1为一字符串常量.若未以"括住,则today_rpt1将被awk解释为一个变量名称.
在awk中任何变量使用之前,并不须事先声明.其初始值为空字符串(Null string)或0.因此程序中若未以"将today_rpt1括住,则today_rpt1将是一变量,其值将是空字符串,这会在执行时造成错误(Unix无法帮您开启一个以空字符串为文件名的文件).
因此在编辑awk程序时,须格外留心.因为若敲错变量名称,awk在编译程序时会认为是一新的变量,并不会察觉.因此往往会造成运行时错误.
BEGIN为awk的保留字,是Pattern的一种.
以BEGIN为Pattern的Actions于awk程序刚被执行尚未读取数据文件时被执行一次,此后便不再被执行.
读者或许觉得本程序中的I/O重定向符号应使用" >>" (append)而非" >".
本程序中若使用">"将数据重导到today_rpt1, awk第一次执行该指令时会产生一个新档today_rpt1,其后再执行该指令时则把数据追加到today_rpt1文件末,并非每执行一次就重开一个新文件.
若采用">>"其差异仅在第一次执行该指令时,若已存在today_rpt1则awk将直接把数据append在原文件之末尾.这一点,与UNIX中的用法不同.
Øawk中如何利用系统资源
awk程序中很容易使用系统资源.这包括在程序中途调用Shell命令来处理程序中的部分数据;或在调用Shell命令后将其产生的结果交回awk程序(不需将结果暂存于某个文件).这一过程是借助awk所提供的管道(虽然有些类似Unix中的管道,但特性有些不同),及一个从awk中呼叫Unix的Shell命令的语法来达成的.
[例:]承上题,将数据按员工ID排序后再输出到文件today_rpt2 ,并于表头附加执行时的日期.
[分析: ]
awk提供与UNIX用法近似的pipe,其记号亦为"|".其用法及含意如下:
awk程序中可接受下列两种语法:
[a.语法] awk output指令| "Shell接受的命令"
(如: print $1,$2 | "sort -k 1" )
[b.语法] "Shell接受的命令" | awk input指令
(如: "ls " | getline)
注: awk input指令只有getline一个.
awk output指令有print, printf()二个.
在a语法中, awk所输出的数据将转送往Shell ,由Shell的命令进行处理.以上例而言, print所输出的数据将经由Shell命令"sort -k 1"排序后再送往屏幕(stdout).
上列awk程序中, "print$1, $2"可能反复执行很多次,其输出的结果将先暂存于pipe中,等到该程序结束时,才会一并进行"sort -k 1".
须注意二点:不论print $1, $2被执行几次, "sort -k 1"的执行时间是"awk程序结束时",
"sort -k 1"的执行次数是"一次".
在b语法中, awk将先调用Shell命令.其执行结果将通过pipe送入awk程序,以上例而言, awk先让Shell执行"ls",Shell执行后将结果存于pipe, awk指令getline再从pipe中读取数据.
使用本语法时应留心:以上例而言,awk "立刻"调用Shell来执行"ls",执行次数是一次.
getline则可能执行多次(若pipe中存在多行数据).
除上列a, b二中语法外, awk程序中其它地方如出现像"date", "cls", "ls"...这样的字符串, awk只把它当成一般字符串处理.
建立如下文件并取名为reformat2.awk
#程序reformat2.awk
#这程序用以练习awk中的pipe
BEGIN {
"date" | getline # Shell执行"date". getline取得结果并以$0记录
print " Today is " , $2, $3 >"today_rpt2"
print "=========================" > "today_rpt2"
print " ID Number Arrival Time" >"today_rpt2"
close( "today_rpt2" )
}
{printf( "%s %s\n", $1 ,$2 ) | "sort -k 1 >>today_rpt2"}
执行如下命令:
awk -f reformat2.awk arr.dat
执行后,系统会自动将sort后的数据追加( Append;因为使用" >>")到文件today_rpt2末端. today_rpt2内容如下:
Today is 09月21日
=========================
ID Number Arrival Time
1005 8:12
1006 7:45
1008 8:01
1012 7:46
1025 7:27
1028 7:49
1029 7:57
1034 7:26
1042 7:59
1051 7:51
1052 8:05
1101 7:32
[说明: ]
awk程序由三个主要部分构成:
[ i.] Pattern { Action}指令
[ ii.]函数主体.例如: function double( x ){ return 2*x } (参考第11节Recursive Program )
[ iii.] Comment (以#开头识别之)
awk的输入指令getline,每次读取一列数据.若getline之后
未接任何变量,则所读入之资料将以$0记录,否则以所指定的变量储存之.
[以本例而言] :
执行"date" | getline后, $0之值为"2007年09月21日星期五14:28:02 CST",当$0之值被更新时, awk将自动更新相关的内建变量,如: $1,$2,..,NF.故$2之值将为"09月", $3之值将为"21日".
(有少数旧版的awk不允许即使用者自行更新(update)$0的值,或者更新$0时,它不会自动更新$1,$2,..NF.这情况下,可改用gawk或nawk.否则使用者也可自行以awk字符串函数split()来分隔$0上的数据)
本程序中printf()指令会被执行12次(因为有arr.dat中有12行数据),但读者不用担心数据被重复sort了12次.当awk结束该程序时才会close这个pipe ,此时才将这12行数据一次送往系统,并呼叫"sort -k 1 >> today_rpt2"处理之.
awk提供另一个调用Shell命令的方法,即使用awk函数system("shell命令")
例如:
$ awk '
BEGIN{
system("date > date.dat")
getline < "date.dat"
print "Today is ", $2, $3
}
'
但使用system( "shell命令" )时, awk无法直接将执行中的部分数据输出给Shell命令.且Shell命令执行的结果也无法直接输入到awk中.
TAG:
标题搜索
日历
|
|||||||||
日 | 一 | 二 | 三 | 四 | 五 | 六 | |||
1 | 2 | 3 | 4 | ||||||
5 | 6 | 7 | 8 | 9 | 10 | 11 | |||
12 | 13 | 14 | 15 | 16 | 17 | 18 | |||
19 | 20 | 21 | 22 | 23 | 24 | 25 | |||
26 | 27 | 28 | 29 | 30 | 31 |
我的存档
数据统计
- 访问量: 47807
- 日志数: 70
- 建立时间: 2007-07-05
- 更新时间: 2011-11-08