原创:http://blog.csdn.net/skydreamer01/article/details/3600628
AWK学习笔记
1 AWK简介
AWK是Alfred V.Aho, Peter J.Weinberger,Brian W.Kerninghan三人在1977设计和实施的,最初是为了试验Unix中的grep和sed工具怎样可以一般化地在文本之外还能处理数据(grep和sed主要是文本处理工具,但AWK同时善于处理文本与数据)。AWK名字的来源是创造此语言的三个人的名字首字母缩写。它的主要来源是grep, sed和C。后继者主要有Perl。
AWK非常适合于处理格式化的文本和数据,比如改变数据的格式、验证其合法性、寻找某些属性的项、数字求和、输出数据报表等。数据的结构化越强,使用AWK会越方便。相比于sed,它有字段(sed只有行而没有内置的字段模型)和数字处理功能。相比于C和Perl,由于它自动化了某些处理流程(比如读取文件和分割字段),可以使得某些任务以比C和Perl少得多的代码来完成。
2 AWK编程模型
AWK程序的基本使用语法如下:
awk [-v var=value] -Fre 'pattern { action }' var=value datafile(s)
awk [-v var=value] -Fre -f scriptfile var=value datafile(s)
用命令行与用脚本文件(scriptfie)是等价的。一般来说,若pattern、action的代码量比较大,倾向于使用脚本文件。
对于以上语法说明如下:
l -v 选项定义的变量在脚本运行之前即存在,可以在脚本的BEGIN流程中被调用;
l 命令行参量(不用-v定义的)只有到输入的第一行读入时才有效,即其在BEGIN流程中无效;
l -F选项将字段分割符(FS)设为一个正则表达式re;
l datafile(s)可以是单个文件,也可以是多个文件,可使用正则表达式)。如果用“-”(不包括引号)表示从命令行输入。
l 命令行输入‘pattern{action}’时单引号是必不可少的!不要忘记。pattern和action都是可选的,但是不能都省略。省略pattern时默认匹配所有行;省略action时默认为输出整行。为了分别pattern和action, action需要用{}括起来。
脚本文件的一般格式为:
BEGIN{
…
}
pattern1 { action1 }
pattern2 { action2 }
…
END{
…
}
这也说明了AWK的编程模型:程序开始处理BEGIN流程(一般是设置分隔符、定义变量以及输出信息行等),然后进入主循环,读入数据每一行,本行数据设为$0, 行数为NR(某一文件的行数为FNR),同时根据FS变量将$0切分为NF个字段,分别用$1,$2,…,$NF标识。程序对每一行用pattern进行匹配,若匹配上则运行action。在所有行处理完后, 程序将
处理END流程(一般是进行后期处理,输出综合结果等)。
说明:
l 程序默认的字段分割符(FS)是“[ /t]+”(Tab或空格),输出分割符(OFS)是“ ”,(空格),默认记录分隔符(RS)是“/n”,可根据需要在命令行或BEGIN中修改;
l pattern可以是以下四种格式:
(1) 表达式(expression). 在表达式为真时执行action.
(2) 正则表达式(/regular expression/), 正则表达式匹配上时执行action.
(3) 复合模式(compound pattern),用 &&(AND), ||(OR),!(NOT)和括号组合出的模式,组合模式为真时执行action.
(4) 范围模式(pattern1,pattern2),从pattern1匹配上的行,直到pattern2匹配上的行,包括这两行。如果pattern2一直没有匹配上,则到文件的末尾。
l action默认一行一条指令,可包括多行。如果需要在一行中包括多个语句,需要用分号(;)分割开。但是一行一个语句则不需要加分号。这里有与C语言相同又有不同的地方,请注意区别。
3 常量与转义符
AWK的常量有字符由字符串常量与数值常量组成,字符串常量含引号,而数值常量没有。
AWK的转义符如下:
符号 | 描述 |
/a | 警告字符,通常是 ASCII BEL字符 |
/b | 退行 |
/f | Formfeed |
/n | 换行 |
/r | 回车 |
/t | TAB |
/ddd | 八进制 |
/c | 任何字符 c 比如/"代表" |
4. 变量
AWK的变量包括用户定义变量(User-defined Variables)、系统变量(System Variables)和字段变量(Field Variables)
分别说明如下:
4.1用户定义变量
AWK的变量不需要声明,也不需要初始化,直接使用。每一个变量同时有一个字符值和数值,AWK根据上下文环境决定作为数值或字符串处理,这是AWK极其独特之处。AWK自动将变量初始化为空值,如果用作数字将作为0。如果需要强制使用字符串,可使用number “”(空格在AWK中是字符串连接符),若需要强制用作数值,可以用 string + 0。
另外应注意,在AWK中,$表示字段,用户变量不需要加$,这是AWK与shell或者Perl不同之处!在shell中,变量定义时不加$,再次引用时则需要用$,而在Perl中,无论定义和引用时都需要加$(Perl中$表示标量,另有@和%符号表示数组和Hash变量)。
4.2系统变量
AWK中包括两种系统变量:默认值可被改变的变量和处理过程中变量。AWK的系统变量列表如下:
变量 | 意 义 | 默认值 |
FS | 字段分割符 | “[ /t]+” |
OFS | 输出字段分隔符 | “ ” |
RS | 记录分割符 | ”/n” |
ORS | 输出记录分隔符 | ”/n” |
OFMT | 输出数字格式 | ”%.6g” |
CONVFMT | 控制数值向字符串转换 | ”%.6g” |
|
|
|
NF | 字段数 | - |
NR | 当前输入记录数(总数),只有到END时,NR才等于总记录数 | - |
FNR | 当前文件的相对记录数 |
|
FILENAME | 当前输入文件名 |
|
ARGC | 命令行参数数目 |
|
ARGV | 命令行参数数组 |
|
ARGIND | 当前处理文件的ARGV指数,FILENAME==ARGV[ARGIND]总是真的 |
|
ENVIRON | 环境变量数组 |
|
RSTART | match()匹配到的初始位置 |
|
RLENGTH | match()匹配到的字符串长度 |
|
SUBSEP | 数组分隔符。将(I,J) 转换为 I SUBSEP J, 模拟多维数组用 |
|
ERRNO # | 在getline时遇到错误的字符串 |
|
PROCINFO | awk进程信息数组,指标可以使egid, euid, gid ,pgrpid, pid, ppid, uid, version |
|
RT # | 每一条记录读入时RS匹配到的字符串 |
|
注意:
l ARGV数组由ARGV[0] ,…,ARGV[ARGC-1]组成,第一个元素指标是0而不是1。这与AWK中的一般数组不同,而与C一致。
l ENVIRON数组在shell与AWK的交互中非常有用。
4.3字段变量
从$1,$2一直到$NF,整行用$0标识。注意,如果$0被赋予新值,所有的$1, $2,..和NF都会被重新计算。同样,若$i,被改变,$0将用OFS重新计算。
5 数组
AWK提供一维数组来存储字符串和数值。数组和数组元素不需要声明,也不需要指定元素个数。AWK特别之处是数组下标总是字符串型的,所以AWK数组总是关联数组(Associative Arrays),相当于Perl的Hash Array。这一点是AWK区别于C及Perl之处。C的数组下标是整数,Perl分别普通数组和Hash数组。
遍历数组的命令是:
for (variable in array)
statement
注意数组下标的输出次序是依赖于AWK的实现的!
测试数组中是否存在某元素的命令是:
if ( subscript in A )
注意如果用if (A[subscript] == “” ) 命令,将可能创建一个新元素!
删除数组元素的命是:delete array[subscript]
用 for ( i in array) delete array[i] ; 可以删除所的有元素,但是新版的gawk可以用简单的delete array;命令删除整个数组。
AWK的多维数组是用一维数组来模拟的。比如,可以用
for (i = 1; i <=10; i++)
for (j = 1; j <=10; j++)
arr[i, j ] = 0
if ((i, j) in arr) 方式来使用多维数组。AWK实际用arr[i SUBSEP j]来代替arr[i,j]。
如果要实现多维数组的循环,可以采取以下方式:
for (k in arr) {
split(k, x, SUBSEP)
i=x[1]
j=x[2]
…(i,j)
}
6 操作符
AWK的操作符基本与C语言相同,但也有些例外。按优先级从低至高列表如下:
操 作 | 符 号 | 说 明 |
赋值 | = += -= *= /= %= ^= | ^=是特别的操作符,C没有 |
条件 | ?: |
|
逻辑或 | || |
|
逻辑与 | && |
|
数组元素 | in | if (i in a ) |
匹配 | ~ !~ | C没有 |
关系 | < <= == != >= > |
|
字符串连接 | (blank) | “a” “ab” (=”abc”) |
加减 | + - |
|
乘除求模 | * / % |
|
单目加减 | + - | -x 正负 |
逻辑非 | ! | !$1 |
开方 | ^ (**) | 有些现代版本的awk可以用** |
自加、自减 | ++ -- | ++x x++ |
字段运算符 | $ | $i 第i字段 |
括号 | () |
|
7 流程控制
AWK的流程控制基本沿用C的流程控制语句,但是没有do until, switch语句。AWK的流程控制语句如下:
{ statement }
if (expression) statement
if (expression ) statement1 else statment2
while (expression ) statement
for(expression1; expression2;expression3) statement
do statement while (expression)
for (variable in array) statement (这是AWK特别的对于数组中每个变量循环)
break (退出while for do循环)
continue (下一while for do 循环)
next (AWK特别的指令,开始进入下一主输入循环,处理下一行记录,非常有用)
exit
exit expression (马上进入END流程;如果在END流程内,结束程序,以expression值为返回值)
8 函数
AWK的函数包括系统函数和自定义函数。系统函数又可分为算术函数和字符串函数
8.1 算术函数
函 数 | 说 明 |
|
|
cos(x) |
|
sin(x) |
|
int(x) | 求整,截去而不是四舍五入 |
log(x) |
|
exp(x) |
|
sqrt(x) |
|
atan2(y,x) | Argtan(y/x) |
rand() |
|
srand() |
|
8.2 字符串函数
函 数 | 说 明 |
index(s,t) | 返回t在s中的第一个位置,如果没找到返回0 |
length(s) | 返回s的字符个数 |
substr(s,p) | 返回s中从p开始的所有字符串 |
substr(s,p,n) | 返回s中从p开始的n个字符 |
gsub(r,s) | 替换$0中所有的正则表达式r为字符串s,返回替换数 |
gsub(r,s,t) | 替换字符串t中所有的正则表达式r为字符串s |
sub(r,s) | 替换$0中左边最长的匹配r的子串 |
sub(r,s,t) | 替换t中左边最长的匹配r的子串 |
split(s,a) | 用FS切割字符串s为数组a,返回字段数 |
split(s,a,fs) | 用fs切割字符串s为数组a,返回字段数 |
spintf(fmt,expr-list) | 格式化输出 |
match(s,r[,array]) | 搜索s,匹配r,设置变量RSTART和RLENGTH |
strtonum(str) | GAWK扩展,返回str的数组,可认八进制(0开头)和十六进制(0x开头) |
asort(source|,desc) | GAWK扩展,对数组内容排序,若指定desc,拷贝排序内容到desc,source,返回元素个数 |
asorti(source|,desc) | GAWK扩展。按指标进行排序 |
tolower(string) | 转换为小写 |
toupper(string) | 转换为大写 |
dcgettext(string[,domain], [,category]) | GAWK国际化函数 |
dcngettext(string1,string2, number[,domain][,category] [,category]) | GAWK国际化函数 |
bindtextdomain(directory [,domain] | 指定信息转换信息的目录 |
8.3 字节处理函数
函 数 | 说 明 |
and(v1,v2) | GAWK扩展,与 |
or(v1,v2) | GAWK扩展,或 |
xor(v1,v2) | GAWK扩展,异或 |
compl(val) | GAWK扩展,val的位补函数 |
lshift(val,count) | GAWK扩展,左移 |
rshift(val,count) | GAWK扩展,右移 |
8.4 时间函数
函 数 | 说 明 |
systime() | GAWK扩展,timestamp,1970年开始的时间戳 |
mktime(datespec) | 将“YYYY MM DD HH MM SS [DST]”格式的string转化为时间戳 |
strftime(format [,timestamp]) | 转化timestamp为字符串,默认format是“%a %b %d %H:%M:%S %Z %Y”,格式与ISO C 1999的定义一致。 |
8.5自定义函数
用户定义函数的格式是:
function name(parameter-list){
statement
}
AWK自定义函数的变量是带值传递,数组是以引用方式传递。另一特别的地方是没在参数列表的变量是全局的。因此定义函数私有变量的方式是将其将入到变量列表中。
9 输入输出
9.1输出
AWK的输出语句如下:
语句 | 说 明 |
| 在标准输出输出 $0 |
print expression, expression, … | 以OFS分隔输出expressions |
print expression, expression, … >filename | 输出到文件filename中 |
print expression, expression, … >>filename | 追加到文件filename中 |
print expression, expression, … |command | 输出到command的标准输入中 |
printf(format,expression, expression, …) | 带格式输出 |
printf(format,expression, expression, …) >filename | 带格式输出 |
printf(format,expression, expression, …) >>filename | 带格式输出 |
printf(format,expression, expression, …) |command | 带格式输出 |
close(filename), close(command) | 中断print与filename或command的联系 |
system(command) | 执行任务command |
其中printf的格式定义与C相同。
9.2输入
AWK的输入有命令行文件输入,也有通过getline函数输入。getline函数的使用方法是:
语 句 | 设 置 |
getline | $0, NF, NR, FNR |
getline var | var, NR, FNR |
getline <file | $0, NF |
getline var <file | Var |
cmd |getline | $0, NF |
cmd |getline var | var |
注意:while(getline <”file”) 是危险的使用循环的方式,可能进入死循环(如果”file”不存在的话。安全的使用方式是 while( getline <”file” > 0)。
10 总结
AWK适用于管理小型的个人的数据库、生成报表、验证数据的合法性、生成索引和进行其它文本准备工作以及试验算法。GAWK可以很容易地获取位和字段数据、数据排序以及进行简单的网络通信[2]。尽管AWK是一种小众语言,但是其在格式化数据的处理方面至今起着不可替代的作用。
11 参考文献
【1】 Alfred V.Aho, Brian W.Kerninghan and Peter J.Weinberger. The AWK Programming Language. Addison-Wesley Publishing Company. 1988
【2】 Dale Dougherty and Arnold Robbins. Sed & Awk, 2nd edition. O’Reilly. 1997
【3】 Arnold Robbins. Effective AWK Programming, 3rd edition. O’Reilly. 2001