初识gawk, gawk Notes(1)
第一次看见awk这三个字母,是在某实习童鞋的QQ状态里面,赞同事写的awk如何高效。
昨天在书畅草草看了下IBM官方技术博里的文章,觉得很赞,很激动,但是有点看不太懂,还是自己抱着教科书,老老实实从头看起吧。
哦,这里说一下,最近看的shell相关的东西,都是跟着《A Pratical Guide to Linux Commands,Editors, and Shell Programming》这本书的。
gawk是awk的GNU版本。
> 使用条件
主要是用gawk来处理结构化的数据。
什么是结构化呢,我的理解,就是一个文件里面的内容,可以按照某种记录记录格式,一条记录一条记录地读,同时,每一条记录中间用一个分隔符分成了若干个部分,而且所有记录用同一个分隔符,且所有行用分隔符分出来的部分的数目相同。
当然,gawk可以结构化数据,也可以输出结构化数据。
另外,要提的一点是,gawk在处理数据的时候,不修改原有的数据文件,只是将数据读出来,做处理。
> 语法
gawk [option] [program] [file-list]
gawk [options] –f program-file [file-list]
白话说来,运行gawk有两种方式:
1、直接在shell里面运行。option是运行的选项;program是gawk的代码,要用' ' 括起来,目的是屏蔽shell对里面内容的解析;file-list是用到的数据文件。[option 有哪些?file-list多个文件怎么处理?]
2、将gawk作为独立的文件,调用gawk文件来执行。要用-f来标记是使用的gawk文件,同时program-list是gawk文件的列表。
> 选项(option)
仅列出要紧的几个
-F fs | 将fs作为输入字段分隔符 |
-f program-file | 从program-file 命名文件而不是标准输入读取gawk程序,用户可以多次指定这个选项 |
-v var=value | 将value赋值给var。赋值动作在gawk程序执行之前进行,并用于BEGIN模式。可以在命令行上多次指定这一选项。 |
> 组成单元
好吧,这是我根据自己理解用的一个名词。为什么这么说呢?想想以前的各种语言,C,基本组成单元是函数,除了头文件之外,程序是有各个函数构成的;C++类似;Java,各种方法,所以说,他们的组成单元是 函数 或 方法。
但是gawk呢?如下:
pattern{ action }
每个action用 "{","}"包围起来,同时,"{"要跟在pattern的同一行。
pattern有多种,action各式各样,但是整个gawk的程序,可以按照这样的组成单元,分解开来。
所以说,写gawk程序,就是在源文件里面,构建一个个这样的组成单元。
gawk的组成单元,有两个部分,pattern,action。pattern作为一个模式,决定了action在什么情况下运行,而action,则是当记录符合条件(pattern)时,执行什么样的动作。这里想用一个图来说明下:
图1 gawk处理结构化数据示意图
如图1所示,gawk的程度,就像是在结构数据上的套子,顺着结构数据,从头读到尾,然后根据数据是否满足要求,来决定执行action。这里有一个问题,在gawk文件内,多个组成单元,是怎么样的一个执行顺序,在后面会用实例检验一下。
根据pattern的不同,可以讲组成单元分成3类:
1、系统默认
有两个BEGIN,END。
BEGIN在所有代码开始前执行,也就是说,他不参与pattern的判定。主要是用来初始化一些系统变量等。
END在前面的所有代码执行完毕后执行,也没有pattern的判定。主要是清理一些中间生成的临时数据等等。
2、无pattern
也就是pattern为空,表示所有的结构化数据,每行数据,都要执行这个pattern下的action
3、定义pattern
实际上“定义pattern”和“无pattern”一样,action都是用户来写的,这里之所以区分开,如前面所说,是基于pattern来分的。
这一类,pattern可以是一个条件判断,也可以是一个正则表达式匹配。
> 系统变量
这些变量被gawk占用了,用的时候,直接拿来就是对应的含义:
表1 变量
$0 | 当前记录(作为单个变量) |
$1-$n | 当前记录中的字段 |
FILENAME | 当前输入文件名 |
FS | (Field Split)输入字段字段分隔符 |
NF | (Number of Fields)当前记录的字段数目 |
NR | (Number of current Record)当前记录的记录编号 |
OFS | (Output Field Split)输出字段分隔符(默认为空格) |
ORS | (Output Record Split)输出记录分隔符(默认为换行符) |
RS | (Record Split)输入记录分隔符(默认为换行) |
FS,RS,可以在BEGIN中,根据具体输入文件的不同,进行设置。
OFS,ORS,也可以再BEGIN中,根据输出文件的要求,进行设置。
在程序中,可以通过NF,知道一条记录中有多少个字段,也就决定了$1-$n的n是多少。
在任何时刻都可以修改分隔符的值,具体方法是通过在程序中或者在命令行中使用--assign(-v)选项,将一项新的值指派给对应的变量。
(也就意味着,在输入和输出文件中,都可以出现分隔符不同一的情况)
> 函数
gawk的内置函数,用来操作数字和字符串
表2 函数
length(str) | 返回字符串str的字符个数,如果没有参数str,返回当前记录的字符个数 |
int(num) | 返回num的整数部分 |
index(str1,str2) | 返回str2在str1中的位置,如果str2不存在返回0 |
split(str,arr,del) | 用del做分隔符,将str的元素放置到arr[1]到arr[n]中,返回数组元素个数 |
sprintf(fmt,args) | 根据fmt格式化args并返回格式化后的字符串 |
substr(str,pos,len) | 返回str中从pos开始长度为len个字符的字符串 |
tolower(str) | str字母全部转为小写 |
toupper(str) | str字母全部转为大写 |
> 注释
该行以 # 开头。
> 关联数组
我理解它类似于C#的Dictionary<k,v>,可以用字符串做索引:array[string]=value
在使用的时候,利用for可以实现:for (elem in array) print array[elem]
> 控制结构
if-else,while,for 与C语言的对应项基本一致,完全不同于Shell下的对应项,不再赘述。
初始gawk,就到这里,用一个例子来结束:
数据文件cars记录了一组关于汽车的销售信息(品牌,型号,生产年代,千里里程数,价格),用gawk,写程序,统计下平均使用年限,所有车的平均价格和新车(晚于2000年)的平均价格
图2 数据文件cars
图3 gawk代码
图4 运行结果