AWK使用总结(1) 《AWK手册》阅读整理

主要来自对AWK手册(http://phi.sinica.edu.tw/aspac/reports/94/94011/)的阅读实践整理。

AWK的执行方式

方式一 $ awk -f pay1.awk emp.dat

方式二 $ awk '{ print $2, $3 * $4 }' emp.dat

 

AWK的工作流程

执行 awk,它会反复进行下列四步骤.

1.自动从指定的数据文件中读取一个数据行.

2.自动更新(Update)相关的内建变量之值.: NF, NR, $0...

3.依次执行程序中所有 Pattern { Actions }指令,其中Pattern { Actions }的语法已涵盖这种 " if (条件) {} "的架构

4.当执行完程序中所有 Pattern { Actions },若数据文件中还有未读取的数据,则反复执行步骤1到步骤4.

awk会自动重复进行上述4个步骤,使用者不须于程序中编写这个循 (Loop).

 

PATTERN{ACTIONS}

PATTERN的含义

BEGIN部分Actions程序一开始执行,被执行一次."

END部分Actions仅在程序最后才成立,仅被执行一次

>大于

<小于

>=大于或等于

<=小于或等于

==等于

!=不等于

~ match

!~ not match

ACTIONS的含义

 表达式 ( function calls, assignments..)

 print表达式列表

 printf(格式化字符串,表达式列表)

 if(表达式 )语句 [else语句]

 while(表达式 )语句

 do语句 while(表达式)

 for(表达式;表达式;表达式)语句

 for( variable in array)语句

 delete

[]

for( any in X_arr )

delete X_arr[any]

delete指令一次只能释放数组中的一个元素.

 break

 continue

 next  

执行 next指令时, awk将掠过位于该指令(next)之后的所有指令(包括其

后的所有Pattern { Actions }),接著读取下一笔数据行,继续从第一

Pattern {Actions}执行起.

例子

/^[ \t]*$/ { print "This is a blank line! Do nothinghere !"

next

}

$2 != 0 { print $1, $1/$2 }

 

 exit [表达式]

[1]

现在需要给组装部门员工调薪5%,(组装部门员工之ID"A"开头),所有员工最后之薪资率若仍低于100,则以100.编写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

 

 

内建变量

$0,一字符串,其内容为目前 awk所读入的数据行.

$1,$0上第一个字段的数据.

$2,$0上第二个字段的数据.

$n,$0上第n个字段的数据.

$NF(Number of Fields)为一整数,其值表$0上所存在的字段数目.

$NR(Number ofRecords)为一整数,其值表awk已读入的数据行数目.

$FILENAME

$FS    例如,FS ="[ \t:]+" (改变字段分隔符,使它变为空格或者tab或者冒号。)

数组:使用字符串当数组的下标(index),类似关联数组使用数组前不须宣告数组名及其大小.

[1]

例如 : awk从资料文件 emp.dat中读入第一笔数据行

"A125 Jenny 100 210"之后,程序中:

$0之值将是 "A125 Jenny 100210"

$1之值为 "A125"

$2之值为 "Jenny"

$3之值为 100

$4之值为 210

$NF之值为 4

$NR之值为 1

$FILENAME之值为 "emp.dat"

注意:awk中任何变量使用之前,并不须事先声明.其初始值为空字符串(Null string) 0.

 

awk I/O指令

print, printf( ), getline

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;

[]

BEGIN { print " ID Number Arrival Time" >"today_rpt1"

print "===========================" >"today_rpt1"

}

{ printf(" %s %s\n", $1,$2 ) >"today_rpt1" }

]

[说明]

1awk 程序中,文件名称 today_rpt1的前后须以" (双引号)括住, today_rpt1为一字符串常量.若未以"括住, today_rpt1将被awk释为一个变量名称.

2 awk中的重定向和SHELL中的有区别。

 

 

AWKSHELL的交互

[a.语法] awk output指令 | "Shell接受的命令"( : print $1,$2 | "sort -k 1" )

[b.语法] "Shell接受的命令" | awk input指令( : "ls " | getline)

[c.语法]使用system命令

[1 ]写一个awk程序来打印出线上人数.

将下列程序建文件,命名为 count.awk

BEGIN{

while ( "who" | getline ) n++

print n

}

执行:awk -f count.awk

结果:印出目前在线人数

注意:一、不论 print $1, $2被执行几次, "sort -k 1"的执行时间"awk程序结束时"[a.语法]程序中, "print$1, $2"可能反复执行很多次,其输出的结果将先暂存于 pipe,等到该程序结束时,才会一并进行 "sort -k 1".

二、"sort -k 1"的执行次数是 "一次".

[2]

$ awk'

BEGIN{

system("date > date.dat")

getline < "date.dat"

print "Today is ", $2, $3

}'

但使用 system( "shell命令" ), awk无法直接将执行中的部分数据输出Shell命令. Shell命令执行的结果也无法直接输入到awk

[3]重定向,一次只能对一个文件进行写操作,否则乱套。

#!/bin/sh

awk '

BEGIN{

FS= "[ \t:]+" #改变字段切割的方式

"date" |getline # Shell执行 "date".getline取得结果以$0记录

print " Today is " ,$2, $3 >"today_rpt3"

print"=========================">"today_rpt3"

print " ID Number Arrival Time" >"today_rpt3"

close( "today_rpt3" )

}

{

#已更改字段切割方式, $2表到达小时数, $3表分钟数

arrival = HM_to_M($2, $3)

printf(" %s %s:%s %s\n", $1, $2, $3, arrival >480 ? "*": " " ) | "sort -k 1 >>

today_rpt3"

total += arrival

}

END {

close("today_rpt3")

close("sort -k 1 >> today_rpt3")

printf(" Average arrival time :%d:%d\n",total/NR/60, (total/NR)%60 ) >>

"today_rpt3"

}

functionHM_to_M( hour, min ){

return hour*60 + min

}

' $*

执行:$ ./reformat3 arr.dat

[说明]

1.awk 中提供的 close( )指令,语法如下(有二种) :

close( filename )

close(置于pipe之前的command )

2.为何本程序使用了两个 close( )指令 :

指令 close( "sort -k 1>> today_rpt3" ),其意思为 close程序中置于 "sort-k 1 >> today_rpt3 "之前的 Pipe ,并立刻调用 Shell来执行"sort -k 1 >>today_rpt3". (若未执行这指令, awk 必须于结束该程序时才会进行上述动作;则这12sort后的数据将被 append到文件 today_rpt3"Average arrivaltime : ..."的后方)

因为 Shell排序后的数据也要写到 today_rpt3,所以awk必须先关闭使用中的today_rpt3以使 Shell正确将排序后的数据追加到today_rpt3否则2个不同的 process同时打开一个文件进行输出将会产生不可预期的结果.

读者应留心上述两点,才可正确控制数据输出到文件中的顺序.

指令 close("sort -k 1>> today_rpt3")中字符串 "sort +0n >>today_rpt3" 须与 pipe |后方的 Shell Command名称一字不差,否则awk将视为二个不同的 pipe.读者可于BEGIN{}中先令变量 Sys_call = "sort +0n >> today_rpt3",

程序中再一律以 Sys_call代替该字符串.

 

读取命令行的参数

建立文件如下,命名为 see_arg :

#!/bin/sh

awk '

BEGIN{

for( i=0; i<ARGC ; i++)

print ARGV[i] #依次印出awk所记录的参数

}

' $*

执行:$ ./see_arg first-argsecond-arg

结果:

awk

first-arg

second-arg

 

内建函数

index(原字串,找寻的子字串)

若原字串中含有欲找寻的子字串,则返回该子字串在原字串中第一次出现的位置,若未曾出现该子字串则返回0.

执行 :

$ awk 'BEGIN{ print index("8-12-94","-")}'

结果:

2

length(字串 ) :返回该字串的长度

match(原字串,用以找寻比对的正则表达式 )

awk找到该字串后会依此字串为依据进行下列动作:

设定awk内建变量 RSTART, RLENGTH,并返回 RSTART 之值

RSTART

=合条件的子字串在原字串中的位置.

= 0 ;若未找到合条件的子字串.

RLENGTH

=合条件的子字串长度(贪婪匹配)

= -1 ;若未找到合条件的子字串.

[1]

awk ' BEGIN {

match( "banana", /(an)+/ )

print RSTART, RLENGTH

} '

结果:2 4

split(原字串,数组名称,分隔字符 )

awk将依所指定的分隔字符(field separator)来分隔原字串成一个个的栏(field),并以指定的数组记录各个被分隔的栏位.

[1]:

ArgLst = "5P12p89"

split( ArgLst, Arr, /[Pp]/)

执行结果 : Arr[1]=5, Arr[2]=12, Arr[3]=89

sub(比对用的正则表达式,将替换的新字串,原字串 )

[1]

A = "a6b12anan212.45an6a"

sub( /(an)+[0-9]*/, "[&]", A)

print A

结果输出

ab12[anan212].45an6a

总结:第二个参数"将替换的新字串"中可用"&"来代表"合於条件的子字串"

[2]

awk '

BEGIN {

data = "p12-P34 P56-p61"

while( match( data ,/[0-9]+/) > 0) {

print substr(data, RSTART, RLENGTH )

sub(/[0-9]+/,"",data)

}

}'

总结:通过 sub() match()的搭配使用,可逐次取出原字串中合乎指定条件的所有子字串.

结果输出

12

34

56

61

substr(字串,起始位置 [,长度] )

[1]

$ awk 'BEGIN { print substr("User:Wei-Lin Liu",6)}'

结果印出

Wei-Lin Liu

[2]从文件的 Fullname 中分离出路径档名

awk '

BEGIN{

Fullname = "/usr/local/bin/xdvi"

match( Fullname, /.*\//)

path = substr(Fullname, 1, RLENGTH-1)

name = substr(Fullname, RLENGTH+1)

print "path :", path," name :",name

}

' $*

结果印出

path : /usr/local/bin name : xdvi


参考资料

1简体中文版由 bones7456 (bones7456@gmail.com)整理.原文应该是http://phi.sinica.edu.tw/aspac/reports/94/94011/ 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值