http://fanqiang.chinaunix.net/program/other/2005-09-07/3621.shtml
http://www.tsnc.edu.cn/default/tsnc_wgrj/doc/awk.htm
http://www.aslibra.com/doc/awk.htm [这篇未整理完,非常赞]
awk的处理文本和数据的方式是这样的,它逐行扫描文件,从第一行到最后一行,寻找匹配的特定模式的行,并在这些行上进行你想要的操作。如果没有指定处理动作,则把匹配的行显示到标准输出(屏幕),如果没有指定模式,则所有被操作所指定的行都被处理。
执行awk时, 它会反复进行下列四步骤.
1,自动从指定的数据文件中读取一个数据行.
2,自动更新(Update)相关的内建变量之值. 如 : NF, NR, $0...
3,依次执行程序中 所有 的 Pattern { Actions } 指令.
4,当执行完程序中所有 Pattern { Actions } 时, 若数据文件中还有未读取的数据, 则反复执行步骤1到步骤4.
awk会自动重复进行上述4个步骤, 使用者不须于程序中编写这个循环 (Loop).
例1:显示文本文件mydoc匹配(含有)字符串"sun"的所有行。
$awk '/sun/{print}' mydoc由于显示整个记录(全行)是awk的缺省动作,因此可以省略action项
$awk '/sun/' mydoc例2:下面的示例显示了内置变量和内置函数length()的使用:
该命令行将显示文本myfile中所有超过80个字符的行号,在这里,用$0表示整个记录(行),同时,内置变量NR不使用标志符'$'。
$awk 'length($0)>80 {print NR}' myfile例3:作为一个较为实际的例子,我们假设要对UNIX中的用户进行安全性检查,方法是考察/etc下的passwd文件,检查其中的passwd字段(第二字段)是否为"*",如不为"*",则表示该用户没有设置密码,显示出这些用户名(第一字段)。我们可以用如下语句实现:
$ awk -F: '$2!="x" {printf("%s no password!\n",$1)}' /etc/passwd例4:如何把一行竖排的数据转换成横排?
awk '{printf("%s,",$1)}' filename
awk允许将一段awk程序写入如下awk脚本文件,注意这里就是把上述的单引号之间的pattern {action}内容写入文本,注意,原命令行中是:$ awk 'length($2)>length($1){print $1 "," $2}' fileName:
length($2)>length($1){print $1 "," $2}
然后在awk命令行中用-f选项调用并执行这段程序。
$awk -f awkScript fileName
与其它UNIX命令一样,awk拥有自己的语法:
awk [ -F re] [parameter...] ['prog'] [-f progfile][in_file...]参数说明:
允许awk更改其字段分隔符。 如 "-F :"表示以冒号":"来进行分割。不指定时默认以空格来分割。
可以同时使用多个域分隔符(用正则匹配),这时应该把分隔符写成放到方括号中,如$awk -F'[:\t]' '{print $1,$3}' test,表示以空格、冒号和tab作为分隔符。
该参数帮助为不同的变量赋值。
程序段可以由多个组成为:pattern {action} pattern {action}...
awk的程序语句段。这个语句段必须用单拓号:'和'括起,以防被shell解释。这个程序语句段的标准形式为: 'pattern {action}'
pattern可以是以下任意一个:
/正则表达式/:使用通配符的扩展集,例如/[Nn]ice/ 关系表达式:关系运算符进行操作,如length($2)>length($1)选择第二个字段比第一个字段长的行。 模式匹配表达式:用运算符~(匹配)和~!(不匹配),如"banana" ~ /an/ 为真 范围模板:模式,模式:指定一个行的范围。该语法不能包括BEGIN和END模式。如awk '/root/,/mysql/' test将显示root第一次出现到mysql第一次出现之间的所有行 BEGIN:让用户指定在第一条输入记录被处理之前所发生的动作,通常可在这里设置全局变量。 END:让用户在最后一条输入记录被读取之后发生的动作。
【注1】范围模板匹配从第一个模板的第一次出现到第二个模板的第一次出现之间所有行。如果有一个模板没出现,则匹配到开头或末尾。如$ awk '/root/,/mysql/' test将显示root第一次出现到mysql第一次出现之间的所有行。
例如,显示第一个匹配Sun或sun的行与第一个匹配Moon或moon的行之间的行,并显示到标准输出上。;
$awk '/[Ss]un/,/[Mm]oon/ {print}' myfile【注2】模式匹配awk 提供的 ~ (match) 及 !~(not match) 二个关系运算符.
其用法与涵义如下:
若 A 为一字符串, B 为一正则表达式(Regular Expression)
A ~ B 判断 字符串A 中是否 包含 能匹配(match)B表达式的子字符串.
A !~ B 判断 字符串A 中是否 不包含 能匹配(match)B表达式的子字符串.
【awk的常规表达式元字符 】 \ 换码序列 ^ 在字符串的开头开始匹配 $ 在字符串的结尾开始匹配 . 与任何单个字符串匹配 [ABC] 与[]内的任一字符匹配 [A-Ca-c] 与A-C及a-c范围内的字符匹配(按字母表顺序) [^ABC] 与除[]内的所有字符以外的任一字符匹配 Desk|Chair 与Desk和Chair中的任一个匹配 [ABC][DEF] 关联。与A、B、C中的任一字符匹配,且其后要跟D、E、F中的任一个字符。 * 与A、B或C中任一个出现0次或多次的字符相匹配 + 与A、B或C中任何一个出现1次或多次的字符相匹配 ? 与一个空串或A、B或C在任何一个字符相匹配 (Blue|Black)berry 合并常规表达式,与Blueberry或Blackberry相匹配
action操作
action参数总是被大括号包围,它由一系统awk语句组成,各语句之间用";"分隔。awk解释它们,并在pattern给定的样式匹配的记录上执行其操作。与shell类似,你也可以使用“#”作为注释符,它使“#”到行尾的内容成为注释,在解释执行时,它们将被忽略。
action由一人或多个命令、函数、表达式组成,之间由换行符或分号隔开,并位于大括号内。主要有四部份:
变量或数组赋值 输出命令 内置函数 控制流命令
你可以省略pattern和action之一,但不能两者同时省略,当省略pattern时没有样式匹配,表示对所有行(记录)均执行操作,省略action时执行缺省的操作——在标准输出上显示。
允许awk调用并执行progfile指定有程序文件。progfile是一个文本文件,他必须符合awk的语法。
awk的输入文件,awk允许对多个输入文件进行处理。值得注意的是awk不修改输入文件。如果未指定输入文件,awk将接受标准输入,并将结果显示在标准输出上。awk支持输入输出重定向。
awk环境变量
变量 描述 $n 当前记录的第n个字段,字段间由FS分隔。 $0 完整的输入记录。 ARGC 命令行参数的数目。 ARGIND 命令行中当前文件的位置(从0开始算)。 ARGV 包含命令行参数的数组。 CONVFMT 数字转换格式(默认值为%.6g) ENVIRON 环境变量关联数组。 ERRNO 最后一个系统错误的描述。 FIELDWIDTHS 字段宽度列表(用空格键分隔)。 FILENAME 当前文件名。 FNR 同NR,但相对于当前文件。 FS 字段分隔符(默认是任何空格)。 IGNORECASE 如果为真,则进行忽略大小写的匹配。 NF 当前记录中的字段数。 NR 当前记录数。 OFMT 数字的输出格式(默认值是%.6g)。 OFS 输出字段分隔符(默认值是一个空格)。 ORS 输出记录分隔符(默认值是一个换行符)。 RLENGTH 由match函数所匹配的字符串的长度。 RS 记录分隔符(默认是一个换行符)。 RSTART 由match函数所匹配的字符串的第一个位置。 SUBSEP 数组下标分隔符(默认值是\034)。
(1,1)%(1,2)%(1,3)%(1,4)%(1,5)%(1,6)%(1,7)%(1,8)%(1,9) (2,1)%(2,2)%(2,3)%(2,4)%(2,5)%(2,6)%(2,7)%(2,8)%(2,9) (3,1)%(3,2)%(3,3)%(3,4)%(3,5)%(3,6)%(3,7)%(3,8)%(3,9) (4,1)%(4,2)%(4,3)%(4,4)%(4,5)%(4,6)%(4,7)%(4,8)%(4,9) (5,1)%(5,2)%(5,3)%(5,4)%(5,5)%(5,6)%(5,7)%(5,8)%(5,9) (6,1)%(6,2)%(6,3)%(6,4)%(6,5)%(6,6)%(6,7)%(6,8)%(6,9) (7,1)%(7,2)%(7,3)%(7,4)%(7,5)%(7,6)%(7,7)%(7,8)%(7,9) (8,1)%(8,2)%(8,3)%(8,4)%(8,5)%(8,6)%(8,7)%(8,8)%(8,9) (9,1)%(9,2)%(9,3)%(9,4)%(9,5)%(9,6)%(9,7)%(9,8)%(9,9)显示文本文件1.txt中第3行到第6行中以字符%分隔的第一字段,第三字段和第七字段
$ awk -F % 'NR==3,NR==6{print $1 $3 $7}' ./1.txt (3,1)(3,3)(3,7) (4,1)(4,3)(4,7) (5,1)(5,3)(5,7) (6,1)(6,3)(6,7)
awk的内置函数
【说明:表中v项表示第一个支持变量的工具(下同):A=awk,N=nawk,P=POSIX awk,G=gawk 】
V 函数 用途或返回值 ------------------------------------------------ N gsub(reg,string,target) 每次常规表达式reg匹配时替换target中的string N index(search,string) 返回string中search串的位置 A length(string) 求串string中的字符个数 N match(string,reg) 返回常规表达式reg匹配的string中的位置 N printf(format,variable) 格式化输出,按format提供的格式输出变量variable。 N split(string,store,delim) 根据分界符delim,分解string为store的数组元素 N sprintf(format,variable) 返回一个包含基于format的格式化数据,variables是要放到串中的数据 G strftime(format,timestamp) 返回一个基于format的日期或者时间串,timestmp是systime()函数返回的时间 N sub(reg,string,target) 第一次当常规表达式reg匹配,替换target串中的字符串 A substr(string,position,len) 返回一个以position开始len个字符的子串 P totower(string) 返回string中对应的小写字符 P toupper(string) 返回string中对应的大写字符 A atan(x,y) x的余切(弧度) N cos(x) x的余弦(弧度) A exp(x) e的x幂 A int(x) x的整数部分 A log(x) x的自然对数值 N rand() 0-1之间的随机数 N sin(x) x的正弦(弧度) A sqrt(x) x的平方根 A srand(x) 初始化随机数发生器。如果忽略x,则使用system() G system() 返回自1970年1月1日以来经过的时间(按秒计算)awk之所以成为一种优秀的程序设计语言的原因之一是它吸收了某些优秀的程序设计语言(例如C)语言的许多优点。这些优点之一就是内置函数的使用, awk定义并支持了一系列的内置函数,由于这些函数的使用,使得awk提供的功能更为完善和强大,例如,awk使用了一系列的字符串处理内置函数(这些函数看起来与C语言的字符串处理函数相似,其使用方式与C语言中的函数也相差无几),正是由于这些内置函数的使用,使awk处理字符串的功能更加强大。本文后面的附录中列有一般的awk所提供的内置函数,这些内置函数也许与你的awk版本有些出入,因此,在使用之前,最好参考一下你的系统中的联机帮助。
$ awk -F % '{printf"%03d%s\n",NR,$3}' 1.txt 001(1,3) 002(2,3) 003(3,3) 004(4,3) 005(5,3) 006(6,3) 007(7,3) 008(8,3) 009(9,3)
在awk程序中引用内置变量不需要使用标志符"$"(回忆一下前面讲过的NR的使用)。
内建变量的使用。变量列表在前面已列出,现在举个例子说明一下。$ awk -F: '{IGNORECASE=1; $1 == "MARY"{print NR,$1,$2,$NF}'test,把IGNORECASE设为1代表忽略大小写,打印第一个域是mary的记录数、第一个域、第二个域和最后一个域。
awk允许用户在awk程序语句中定义并调用自已的变量。在awk中引用自定义变量必须在它前面加上标志符"$"。与C语言不同的是,awk中不需要对变量进行初始化,awk根据其在awk中第一次出现的形式和上下文确定其具体的数据类型。当变量类型不确定时,awk默认其为字符串类型。这里有一个技巧:如果你要让你的awk程序知道你所使用的变量的明确类型,你应当在在程序中给它赋初值。
赋值格式:Variable = expression,如$ awk '$1 ~/test/{count = $2 + $3; print count}' test,上式的作用是,awk先扫描第一个域,一旦test匹配,就把第二个域的值加上第三个域的值,并把结果赋值给变量count,最后打印出来。
awk可以在命令行中给变量赋值,然后将这个变量传输给awk脚本。如$ awk -F: -f awkscript month=4 year=2004 test,上式的month和year都是自定义变量,分别被赋值为4和2004。在awk脚本中,这些变量使用起来就象是在脚本中建立的一样。注意,如果参数前面出现test,那么在BEGIN语句中的变量就不能被使用。
awk算术运算符
运算符 用途 ------------------ x^y x的y次幂 x**y 同上 x%y 计算x/y的余数(求模) x+y x加y x-y x减y x*y x乘y x/y x除y -y 负y(y的开关符号);也称一目减 ++y y加1后使用y(前置加) y++ 使用y值后加1(后缀加) --y y减1后使用y(前置减) y-- 使用后y减1(后缀减) x=y 将y的值赋给x x+=y 将x+y的值赋给x x-=y 将x-y的值赋给x x*=y 将x*y的值赋给x x/=y 将x/y的值赋给x x%=y 将x%y的值赋给x x^=y 将x^y的值赋给x x**=y 将x**y的值赋给x
操作符 含义 x==y x等于y x!=y x不等于y x>y x大于y x>=y x大于或等于y x<y x小于y x<=y x小于或等于y? x~re x匹配正则表达式re? x!~re x不匹配正则表达式re?
比较表达式:conditional expression1 ? expression2: expression3,
例如:
$ awk '{max = {$1 > $3} ? $1: $3: print max}' test。如果第一个域大于第三个域,$1就赋值给max,否则$3就赋值给max。 $ awk '$1 + $2 < 100' test。如果第一和第二个域相加大于100,则打印这些行。 $ awk '$1 > 5 && $2 < 10' test,如果第一个域大于5,并且第二个域小于10,则打印这些行。
在awk中两个特别的表达式,BEGIN和END,这两者都可用于pattern中(参考前面的awk语法),提供BEGIN和END的作用是给程序赋予初始状态和在程序结束之后执行一些扫尾的工作。任何在BEGIN之后列出的操作(在{}内)将在awk开始扫描输入之前执行,而END之后列出的操作将在扫描完全部的输入之后执行。因此,通常使用BEGIN来显示变量和预置(初始化)变量,使用END来输出最终结果。
BEGIN模块后紧跟着动作块,这个动作块在awk处理任何输入文件之前执行。所以它可以在没有任何输入的情况下进行测试。它通常用来改变内建变量的值,如OFS,RS和FS等,以及打印标题。如:$ awk 'BEGIN{FS=":"; OFS="\t"; ORS="\n\n"}{print $1,$2,$3}’ test。上式表示,在处理输入文件以前,域分隔符(FS)被设为冒号,输出文件分隔符(OFS)被设置为制表符,输出记录分隔符(ORS)被设置为两个换行符。$ awk 'BEGIN{print "TITLE TEST"}‘只打印标题。
END不匹配任何的输入文件,但是执行动作块中的所有动作,它在整个输入文件处理完成后被执行。如$ awk 'END{print "The number of records is" NR}' test,上式将打印所有被处理的记录数。
awk分支结构允许嵌套
if (expression){ statement; statement; ... } else{ statement; statement; ... }
例如:
$ awk '{if ($1 <$2) print $2 "too high"}' test。如果第一个域小于第二个域则打印。 $ awk '{if ($1 < $2) {count++; print "ok"}}' test.如果第一个域小于第二个域,则count加一,并打印ok。
while(expression) { statement; statement; ... }
变量的初始值为1,若i小于可等于NF(记录中域的个数),则执行打印语句,且i增加1。直到i的值大于NF. $ awk '{ i = 1; while ( i <= NF ) { print NF,$i; i++}}' test。 $ awk '{for (i = 1; i<NF; i++) print NF,$i}' test。
do { 语句 }while(条件判断语句)
for(初始表达式;终止条件;步长表达式) {语句}
在awk的 while、do-while和for语句中允许使用break,continue语句来控制流程走向,也允许使用exit这样的语句来退出。break中断当前正在执行的循环并跳到循环外执行下一条语句。continue从当前位置跳到循环开始处执行。对于exit的执行有两种情况:当exit语句不在END中时,任何操作中的exit命令表现得如同到了文件尾,所有模式或操作执行将停止,END模式中的操作被执行。而出现在END中的exit将导致程序终止。
breadkcontinue语句。break用于在满足条件的情况下跳出循环;continue用于在满足条件的情况下忽略后面的语句,直接返回循环的顶端。如:
{for ( x=3; x<=NF; x++) if ($x<0){print "Bottomed out!"; break}} {for ( x=3; x<=NF; x++) if ($x==0){print "Get next item"; continue}}next语句从输入文件中读取一行,然后从头开始执行awk脚本。如:
{if ($1 ~/test/){next} else {print} }exit语句用于结束awk程序,但不会略过END块。退出状态为0代表成功,非零值表示出错。
awk中的数组的下标可以是数字和字母,称为关联数组。
用变量作为数组下标。如:
$ awk {name[x++]=$2};END{for(i=0;i<NR;i++) print i,name[i]}' test。
数组name中的下标是一个自定义变量x,awk初始化x的值为0,在每次使用后增加1。第二个域的值被赋给name数组的各个元素。在END模块中,for循环被用于循环整个数组,从下标为0的元素开始,打印那些存储在数组中的值。因为下标是关健字,所以它不一定从0开始,可以从任何值开始。
special for循环用于读取关联数组中的元素。格式如下:
{for (item in arrayname){ print arrayname[item] } }打印有值的数组元素。打印的顺序是随机的:
$ awk '/^tom/{name[NR]=$1}; END{for(i in name){print name[i]}}' test。
用字符串作为下标。如:count["test"]
用域值作为数组的下标。一种新的for循环方式:
for (index_value in array) statement
如以下语句将打印$1中字符串出现的次数。它首先以第一个域作数组count的下标,第一个域变化,索引就变化。
$ awk '{count[$1]++} END{for(name in count) print name,count[name]}' test
delete函数用于删除数组元素。如下分配给数组line的是第一个域的值,所有记录处理完成后,special for循环将删除每一个元素:
$ awk '{line[x++]=$1} END{for(x in line) delete(line[x])}' test
function 函数名(参数表){ 函数体 }在gawk中允许将function省略为func,但其它版本的awk不允许。函数名必须是一个合法的标志符,参数表中可以不提供参数(但在调用函数时函数名后的一对括号仍然是不可缺少的),也可以提供一个或多个参数。与C语言相似,awk的参数也是通过值来传递的。 在awk中调用函数比较简单,其方法与C语言相似,但awk比C语言更为灵活,它不执行参数有效性检查。换句话说,在你调用函数时,可以列出比函数预计(函数定义中规定)的多或少的参数,多余的参数会被awk所忽略,而不足的参数,awk将它们置为缺省值0或空字符串,具体置为何值,将取决于参数的使用方式。
nawk >'BEGIN{pageno=1;file=FILENAME >pageno=print_header(file,pageno);#调用函数print_header >printf("当前页页号是:%d\n",pageno); >} >#定义函数print_header >function print_header(FileName,PageNum){ >printf("%s %d\n",FileName,PageNum); >PageNum++;return PageNUm; >} >}' myfile
为便于解释awk程序架构, 及有关术语(terminology), 先以一个员工薪资档(emp.dat ), 来加以介绍.
A125 Jenny 100 210 A341 Dan 110 215 P158 Max 130 209 P148 John 125 220 A123 Linda 95 210文件中各字段依次为 员工ID, 姓名, 薪资率,及 实际工时. ID中的第一码为部门识别码. "A","P"分别表示"组装"及"包装"部门.
[ 范例 :] 以文件 emp.dat 为例, 计算每人应发工资并打印报表.
[ 分析 :] awk 会自行一次读入一列数据, 故程序中仅需告诉awk 如何处理所读入的数据行.
$ awk '{ print $2, $3 * $4 }' emp.dat Jenny 21000 Dan 23650 Max 27170 John 27500 Linda 19950
如果是awk命令行,则执行:
$ awk '$1 ~ /^A.*/ { $3 *= 1.05 } $3<100 { $3 = 100 } { printf("%s %8s %d\n", $1, $2, $3)}' emp.dat其中单引号之间由三组构成:‘pattern {action}pattern {action}pattern {action}',现在如果要写入awk脚本文件,只需将单引号中的内容写入。
编写如下之程序, 并取名 adjust1.awk
$1 ~ /^A.*/ { $3 *= 1.05 } $3<100 { $3 = 100 } { printf("%s %8s %d\n", $1, $2, $3)}执行下列命令 :
$awk -f adjust1.awk emp.dat A125 Jenny 105 A341 Dan 115 P158 Max 130 P148 John 125 A123 Linda 100说 明 :
1,从数据文件中每次读入一个数据行, 依序执行完程序中所有的 Pattern{ Action }指令:
2,第一个Pattern{ Action }指令是:$1~/^A.*/ { $3 *= 1.05 }
用来判断该笔数据行的第一栏是否包含以"A"开头的子字符串. 其中 /^A.*/ 是一个Regular Expression, 用以表示任何以"A"开头的字符串. (有关 Regular Expression 之用法 参考 附录 E ).
3,第二个Pattern{ Action }指令是:$3 < 100 { $3 = 100 }
若第三栏的数据内容(表薪资率)小于100, 则调整为100.
4,第三个Pattern{ Action }指令是:{printf("%s %8s %d\n",$1,$2,$3)}
省略了Pattern(无条件执行Actions), 故所有数据行调整后的数据都将被印出.
5,再从数据文件中读进下一笔记录继续进行处理.
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
例如: 希望用数组来记录 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."]++ .
for(i=0; i<100; i++) printf("%d\n", Arr[i]);即可. 上式中:
for(course in Number){....}指定用 course 来记录 awk 从Number[ ] 中所找到的下标. awk每找到一个下标时, 就用course记录该下标之值且执行{....}中之指令. 藉由这个方式便可取出数组中储存的信息.
{ 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 O.S. 2 A.I. 1 Algorithm 2 D.S. 1 Graphics 2 Discrete 3 Arch. 2[ 说 明 : ]
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正处理的数据行中, 所包含的字段个数.
BEGIN { while ( "who" | getline ) n++ print n }并执行下列命令 :
awk -f count.awk执行结果将会印出目前在线人数
[7.1] 在到班资料文件 arr.dat 之前增加一行抬头
"ID Number Arrvial Time", 并产生报表输出到文件today_rpt1 中.
< 思考: 在awk中如何将数据输出到文件 >
[7.2]将 today_rpt1 上的数据按员工代号排序, 并加注执行当日日期; 产生文件 today_rpt2
<思考 awk中如何运用系统资源及awk中Pipe之特性 >
[7.3] 将awk程序包含在一个shell script文件中
[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
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中的用法不同.
[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 ' BEGIN{ system("date > date.dat") getline < "date.dat" print "Today is ", $2, $3 } '但使用 system( "shell 命令" ) 时, awk无法直接将执行中的部分数据输出给Shell 命令. 且 Shell 命令执行的结果也无法直接输入到awk中.
{print}这个程序执行时会把数据文件的内容 print 到屏幕上( 与cat功用类似 ).
#!/bin/sh # 注意以下的 awk 与 ' 之间须有空白隔开 awk ' {print} ' $* # 注意以上的 ' 与 $* 之间须有空白隔开执行 mydisplay 之前, 须先将它改成可执行的文件(此步骤往后不再赘述). 请执行如下命令:
$ chmod +x mydisplay往后使用者就可直接把 mydisplay 当成指令, 来display任何文件.
$ ./mydisplay today_rpt1 today_rpt2[ 说 明 : ]
ps ux |awk '{for(i=3;i<=NF;i++) printf $i" ";printf "\n"}'或者
ps ux |cut -d " " -f 3-
cut -d " " -f 3- input_filename > output_filename
....