awk 使用手册.docx
《awk 使用手册.docx》由会员分享,可在线阅读,更多相关《awk 使用手册.docx(79页珍藏版)》请在冰豆网上搜索。
awk使用手册
1.前言
有关本手册:
这是一本awk学习指引,其重点着重于:
l awk适于解决哪些问题?
l awk常见的解题模式为何?
为使读者快速掌握awk解题的模式及特性,本手册系由一些较具代表性的范例及其题解所构成;各范例由浅入深,彼此间相互连贯,范例中并对所使用的awk语法及指令辅以必要的说明.有关awk的指令,函数,...等条列式的说明则收录于附录中,以利读者往后撰写程序时查阅.如此编排,可让读者在短时间内顺畅地学会使用awk来解决问题.建议读者循着范例上机实习,以加深学习效果.
读者宜先具备下列背景:
[a.]UNIX环境下的简单操作及基本概念.
例如:
文件编辑,文件复制及管道,输入/输出重定向等概念
[b.]C语言的基本语法及流程控制指令.
(awk指令并不多,且其中之大部分与C语言中之用法一致,本手册中对该类指令之语法及特性不再加以繁冗的说明,读者若欲深究,可自行翻阅相关的C语言书籍)
2.awk概述
为什么使用awk
awk是一种程序语言.它具有一般程序语言常见的功能.
因awk语言具有某些特点,如:
使用直译器(Interpreter)不需先行编译;变量无类型之分(Typeless),可使用文字当数组的下标(AssociativeArray)...等特色.因此,使用awk撰写程序比起使用其它语言更简洁便利且节省时间.awk还具有一些内建功能,使得awk擅于处理具数据行(Record),字段(Field)型态的资料;此外,awk内建有pipe的功能,可将处理中的数据传送给外部的Shell命令加以处理,再将Shell命令处理后的数据传回awk程序,这个特点也使得awk程序很容易使用系统资源.
由于awk具有上述特色,在问题处理的过程中,可轻易使用awk来撰写一些小工具;这些小工具并非用来解决整个大问题,它们只扮演解决个别问题过程的某些角色,可藉由Shell所提供的pipe将数据按需要传送给不同的小工具进行处理,以解决整个大问题.这种解题方式,使得这些小工具可因不同需求而被重复组合及重用(reuse);也可藉此方式来先行测试大程序原型的可行性与正确性,将来若需要较高的执行速度时再用C语言来改写.这是awk最常被应用之处.若能常常如此处理问题,读者可以以更高的角度来思考抽象的问题,而不会被拘泥于细节的部份.
本手册为awk入门的学习指引,其内容将先强调如何撰写awk程序,未列入进一步解题方式的应用实例,这部分将留待UNIX进阶手册中再行讨论.
如何取得awk
一般的UNIX操作系统,本身即附有awk.不同的UNIX操作系统
所附的awk其版本亦不尽相同.若读者所使用的系统上未附有awk,
可透过anonymousftp到下列地方取得:
phi.sinica.edu.tw:
/pub/gnu
ftp.edu.tw:
/UNIX/gnu
prep.ai.mit.edu:
/pub/gnu
awk如何工作
为便于解释awk程序架构,及有关术语(terminology),先以一个员工薪资档(emp.dat),来加以介绍.
A125Jenny100210
A341Dan110215
P158Max130209
P148John125220
A123Linda95210
文件中各字段依次为员工ID,姓名,薪资率,及实际工时.ID中的第一码为部门识别码."A","P"分别表示"组装"及"包装"部门.
本小节着重于说明awk程序的主要架构及工作原理,并对一些重要的名词辅以必要的解释.由这部分内容,读者可体会出awk语言的主要精神及awk与其它语程序言的差异处.为便于说明,以条列方式说明于后.
名词定义
l 数据行:
awk从数据文件上读取数据的基本单位.以上列文件emp.dat为例,awk读入的
第一笔数据行是"A125Jenny100210"
第二笔数据行是"A341Dan110215"
一般而言,一个数据行就相当于数据文件上的一行资料.(参考:
附录B内建变量"RS")
l 字段(Field):
为数据行上被分隔开的子字符串.
以数据行"A125Jenny100210"为例,
第一栏第二栏第三栏第四栏"A125""Jenny"100210
一般是以空格符来分隔相邻的字段.(参考:
附录D内建变量"FS")
3.如何执行awk
于UNIX的命令行上键入诸如下列格式的指令:
("$"表Shell命令行上的提示符号)
$awk'awk程序'数据文件文件名
则awk会先编译该程序,然后执行该程序来处理所指定的数据文件.
(上列方式系直接把程序写在UNIX的命令行上)
awk程序的主要结构:
awk程序中主要语法是Pattern{Actions},故常见之awk程序其型态如下:
Pattern1{Actions1}
Pattern2{Actions2}
......
Pattern3{Actions3}
Pattern是什么?
awk可接受许多不同型态的Pattern.一般常使用"关系表达式"(Relationalexpression)来当成Pattern.
例如:
x>34是一个Pattern,判断变量x与34是否存在大于的关系.
x==y是一个Pattern,判断变量x与变量y是否存在等于的关系.
上式中x>34,x==y便是典型的Pattern.
awk提供C语言中常见的关系运算符(RelationalOperators)如
>,<,>=,<=,==,!
=
此外,awk还提供~(match)及!
~(notmatch)二个关系运算符(注一).
其用法与涵义如下:
若A为一字符串,B为一正则表达式(RegularExpression)
A~B判断字符串A中是否包含能匹配(match)B表达式的子字符串.
A!
~B判断字符串A中是否不包含能匹配(match)B表达式的子字符串.
例如:
"banana"~/an/整个是一个Pattern.
因为"banana"中含有可以匹配/an/的子字符串,故此关系式成立(true),整个Pattern的值也是true.
相关细节请参考附录APatterns,附录ERegularExpression
(注一:
)有少数awk论著,把~,!
~当成另一类的Operator,并不视为一种RelationalOperator.本手册中将这两个运算符当成一种RelationalOperator.
Actions是什么?
Actions是由许多awk指令构成.而awk的指令与C语言中的指令十分类似.
例如:
awk的I/O指令:
print,printf(),getline...
awk的流程控制指令:
if(...){..}else{..},while(...){...}...
(请参考附录B---"Actions")
awk如何处理Pattern{Actions}?
awk会先判断(Evaluate)该Pattern的值,若Pattern判断后的值为true(或不为0的数字,或不是空的字符串),则awk将执行该Pattern所对应的Actions.反之,若Pattern之值不为true,则awk将不执行该Pattern所对应的Actions.
例如:
若awk程序中有下列两指令
50>23{print"Hello!
Theword!
!
"}
"banana"~/123/{print"Goodmorning!
"}
awk会先判断50>23是否成立.因为该式成立,所以awk将印出"Hello!
Theword!
!
".而另一Pattern为"banana"~/123/,因为"banana"内未含有任何子字符串可match/123/,该Pattern之值为false,故awk将不会印出"Goodmorning!
"
awk如何处理{Actions}的语法?
(缺少Pattern部分)
有时语法Pattern{Actions}中,Pattern部分被省略,只剩{Actions}.这种情形表示"无条件执行这个Actions".
awk的字段变量
awk所内建的字段变量及其涵意如下:
字段变量
含义
$0
一字符串,其内容为目前awk所读入的数据行.
$1
$0上第一个字段的数据.
$2
$0上第二个字段的数据.
...
其余类推
读入数据行时,awk如何更新(update)这些内建的字段变量?
当awk从数据文件中读取一个数据行时,awk会使用内建变量$0予以记录.每当$0被改动时(例如:
读入新的数据行或自行变更$0,...)awk会立刻重新分析$0的字段情况,并将$0上各字段的数据用$1,$2,..予以记录.
awk的内建变量(Built-inVariables)
awk提供了许多内建变量,使用者于程序中可使用这些变量来取得相关信息.常见的内建变量有:
内建变量
含义
NF(NumberofFields)
为一整数,其值表$0上所存在的字段数目.
NR(NumberofRecords)
为一整数,其值表awk已读入的数据行数目.
FILENAMEawk
正在处理的数据文件文件名.
例如:
awk从资料文件emp.dat中读入第一笔数据行
"A125Jenny100210"之后,程序中:
$0之值将是"A125Jenny100210"
$1之值为"A125"
$2之值为"Jenny"
$3之值为100
$4之值为210
$NF之值为4
$NR之值为1
$FILENAME之值为"emp.dat"
awk的工作流程:
执行awk时,它会反复进行下列四步骤.
1.自动从指定的数据文件中读取一个数据行.
2.自动更新(Update)相关的内建变量之值.如:
NF,NR,$0...
3.依次执行程序中所有的Pattern{Actions}指令.
4.当执行完程序中所有Pattern{Actions}时,若数据文件中还有未读取的数据,则反复执行步骤1到步骤4.
awk会自动重复进行上述4个步骤,使用者不须于程序中编写这个循环(Loop).
打印文件中指定的字段数据并加以计算
awk处理数据时,它会自动从数据文件中一次读取一笔记录,并会
将该数据切分成一个个的字段;程序中可使用$1,$2,...直接取得
各个字段的内容.这个特色让使用者易于用awk编写reformatter来改变量据格式.
[范例:
]以文件emp.dat为例,计算每人应发工资并打印报表.
[分析:
]awk会自行一次读入一列数据,故程序中仅需告诉
awk如何处理所读入的数据行.
执行如下命令:
($表UNIX命令行上的提示符)
$awk'{print$2,$3*$4}'emp.dat
执行结果如下:
屏幕出现:
Jenny21000
Dan23650
Max27170
John27500
Linda19950
[说明:
]
UNIX命令行上,执行awk的语法为:
$awk'awk程序'欲处理的资料文件文件名
本范例中的程序部分为{print$2,$3*$4}.
把程序置于命令行时,程序之前后须以'括住.
emp.dat为指定给该程序处理的数据文件文件名.
本程序中使用:
Pattern{Actions}语法.
Pattern部分被省略,表无任何限制条件.故awk读入每笔数据行后都将无条件执行这个Actions.
print为awk所提供的输出指令,会将数据输出到stdout(屏幕).
print的参数间彼此以","(逗号)隔开,印出数据时彼此间会以空白隔开.(参考附录D内建变量OFS)
将上述的程序部分储存于文件pay1.awk中.执行命令时再指定awk程序文件之文件名.这是执行awk的另一种方式,特别适用于程序较大的情况,其语法如下:
$awk-fawk程序文件名数据文件文件名
故执行下列两命令,将产生同样的结果.
$awk-fpay1.awkemp.dat
$awk'{print$2,$3*$4}'emp.dat
读者可使用"-f"参数,让awk主程序使用“其它仅含awk函数的文件中的函数”
其语法如下:
$awk-fawk主程序文件名-fawk函数文件名数据文件文件名
(有关awk中函数的声明与使用于7.4中说明)
awk中也提供与C语言中类似用法的printf()函数.使用该函数可进一步控制数据的输出格式.
编辑另一个awk程序如下,并取名为pay2.awk
{printf("%6sWorkhours:
%3dPay:
%5d\n",$2,$3,$3*$4)}
执行下列命令
$awk-fpay2.awkemp.dat
执行结果屏幕出现:
JennyWorkhours:
100Pay:
21000
DanWorkhours:
110Pay:
23650
MaxWorkhours:
130Pay:
27170
JohnWorkhours:
125Pay:
27500
LindaWorkhours:
95Pay:
19950
4.选择符合指定条件的记录
Pattern{Action}为awk中最主要的语法.若某Pattern之值为真则执行它后方的Action.awk中常使用"关系表达式"(RelationalExpression)来当成Pattern.
awk中除了>,<,==,!
=,...等关系运算符(RelationalOperators)外,另外提供~(match),!
~(NotMatch)二个关系运算符.利用这两个运算符,可判断某字符串是否包含能匹配所指定正则表达式的子字符串.由于这些特性,很容易使用awk来编写需要字符串比对,判断的程序.
[范例:
]承上例,
组装部门员工调薪5%,(组装部门员工之ID以"A"开头)
所有员工最后之薪资率若仍低于100,则以100计.
编写awk程序打印新的员工薪资率报表.
[分析]:
这个程序须先判断所读入的数据行是否合于指定条件,再进行某些动作.awk中Pattern{Actions}的语法已涵盖这种"if(条件){动作}"的架构.编写如下之程序,并取名adjust1.awk
$1~/^A.*/{$3*=1.05}$3<100{$3=100}
{printf("%s%8s%d\n",$1,$2,$3)}
执行下列命令:
$awk-fadjust1.awkemp.dat
结果如下:
屏幕出现:
A125 Jenny105
A341 Dan115
P158 Max130
P148 John125
A123 Linda100
说明:
awk的工作程序是:
从数据文件中每次读入一个数据行,依序执行完程序中所有的Pattern{Action}指令:
$1~/^A.*/{$3*=1.05}
$3<100{$3=100}
{printf("%s%8s%d\n",$1,$2,$3)}
再从数据文件中读进下一笔记录继续进行处理.
第一个Pattern{Action}是:
$1~/^A.*/{$3*=1.05}
$1~/^A.*/是一个Pattern,用来判断该笔数据行的第一栏是否包含以"A"开头的子字符串.其中/^A.*/是一个RegularExpression,用以表示任何以"A"开头的字符串.(有关RegularExpression之用法参考附录E).
Actions部分为$3*=1.05
$3*=1.05与$3=$3*1.05意义相同.运算子"*="之用法则与C语言中一样.此后与C语言中用法相同的运算子或语法将不予赘述.
第二个Pattern{Actions}是:
$3<100{$3=100}若第三栏的数据内容(表薪资率)小于100,则调整为100.
第三个Pattern{Actions}是:
{printf("%s%8s%d\n",$1,$2,$3)}省略了Pattern(无条件执行Actions),故所有数据行调整后的数据都将被印出.
5.awk中数组
awk程序中允许使用字符串当做数组的下标(index).利用这个特色十分有助于资料统计工作.(使用字符串当下标的数组称为AssociativeArray)
首先建立一个数据文件,并取名为reg.dat.此为一学生注册的资料文件;第一栏为学生姓名,其后为该生所修课程.
MaryO.S.Arch.Discrete
SteveD.S.AlgorithmArch.
WangDiscreteGraphicsO.S.
LisaGraphicsA.I.
LilyDiscreteAlgorithm
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语言为例,声明intArr[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(courseinNumber){....}
指定用course来记录awk从Number[]中所找到的下标.awk每找到一个下标时,就用course记录该下标之值且执行{....}中之指令.藉由这个方式便可取出数组中储存的信息.
(详见下例)
[范例:
]统计各科修课人数,并印出结果.
建立如下程序,并取名为course.awk:
{for(i=2;i<=NF;i++)Number[$i]++}
END{for(courseinNumber)printf("%10s%d\n",course,Number[course])}
执行下列命令:
$awk-fcourse.awkreg.dat
执行结果如下:
Graphics2
O.S.2
Discrete3
A.I.1
D.S.1
Arch.2
Algorithm2
[说明:
]
这程序包含二个Pattern{Actions}指令.
{for(i=2;i<=NF;i++)Number[$i]++}
END{for(courseinNumber)printf("%10s%d\n",course,Number[course])}
第一个Pattern{Actions}指令中省略了Pattern部分.故随着
每笔数据行的读入其Actions部分将逐次无条件被执行.
以awk读入第一笔资料"MaryO.S.Arch.Discrete"为例,因为该笔数据NF=4(有4个字段),故该Action的forLoop中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仅被执行