AWK、AWK 关联数组(associative arrays)AWK是一种优秀的文本处理工具,它不仅是 Linux 中也是很多其它 Unix 环境中现有的功能最强大的数据处理工具之一。AWK 提供了极其强大的功能:可以进行样式装入、流控制、数学运算、进程控制语句以及内置的变量和函数。它具备了一个完整语言所具有的几乎所有特性。 在 awk 中数组叫做关联数组(associative arrays),因为下标记可以是数也可以是串。awk 中的数组不必提前声明,也不必声明大小。数组元素用 0 或空串来初始化,这根据上下文而定。例如:
1:可以用数值作数组索引(下标)
1
2
| Myarray[1]=”xu sihua”
Myarray[2]=”780927”
|
2:可以用字符串作数组索引(下标)
1
2
3
| Myarray[“first”]=”xu”
Myarray[“last”]=”si hua”
Myarray[“birth”]=”780927”
|
使用中 print myarray[1] 将得到”xu sihua” 而 print myarray[2] 和 print[“birth”] 都将得到 ”780927” 。
问题的提出在我们的生产系统中有一个应用每天都有大量的数据库插入操作,这些操作被记录在了日志文件中,而我们的目的就是从这些日志文件中提取所需要的数据。日志文件的数据格式如下:
1
2
3
| --sert into table_a ( field1,field2,field3 ...) values ( 3,1,'xx',...) ;
--sert into table_a ( field4,field1,field2 ...) valuse ( 'yyy',2,675 ,...);
--sert into table_a ( field1,field8,fieldN ...) valuse ( 'zzz','ccc','eee' ,...);
|
其中 field1、field2、fieldN 等是数据库表 table_a 中的字段,每条语句中的字段数不定,顺序也不定,但是所有的字段都是表 table_a 内的字段,values 中的值有字符串也有整数。我想通过上面的文件产生下面格式的数据:
1
2
3
| field1_value|field2_value|field3_value|.....|fieldN_value|
field1_value|field2_value|field3_value|.....|fieldN_value|
field1_value|field2_value|field3_value|.....|fieldN_value|
|
具体实现实现思想很简单,我创建一个数组(AWK 中的关联数组),用字段名作为数组的下标(键),用每条记录 Values 中的值来填充这个数组,最后按照自己所需要的格式输出,下面的代码我省略了一些不必要的部分,仅仅是为了把问题讲清楚:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
| BEGIN {
print "\n Extract some data from CDR Data Package. ","\n",
"Processing begins at:",strftime("%Y%m%d%H%M%S", systime())
#为了记录程序的运行时间我在程序开始时输出当前的系统时间,用到了 AWK 的内置函数 strftime.
}
{ #下面创建了数组 ta_record,数组的下标就是记录中的字段名
ta_record["field1"]=""
ta_record["field2"]=""
ta_record["field3"]=""
ta_record["field4"]=""
…………
ta_record["fieldn"]=""
#下面把每条记录中的字段与对应的值存入关联数组中
for (i=5; i<=(NF-1)/2;i++)
{
if(i%2 > 0)
{
value=tolower($(i+(NF-3)/2 ))
if( match(value,/^'/) ) #有些字段的值是用单引号引起来的这个需
#要去掉。
{
#这里用了 AWK 的 tolower 和 substr 函数
ta_record[tolower($i)]=substr(value,2,length(value)-2)
}
else #字段的值如果不带单引号直接赋值即可。
ta_record[tolower($i)]=value
}
}
#根据自己的需要输出数据,可以加多个过滤条件,例如:
#只输出字段 fieldx 的值为 3 和字段 fieldy 的值为 12 的记录
if( (NR+1)%10000 == 0) {printf("%s",". ")}
if(ta_record["fieldx"] == 3 || ta_record["fieldy"] == 12 )
{
printf("%s%s%s%s%s%s%s%s%s%s%s\n",
ta_record["field1"],"|",
ta_record["field2"],"|",
ta_record["field3"],"|",
ta_record["field4"],"|"
,ta_record["field5"],"|",
ta_record["fieldN"],"|")
>> ta_record["fieldz"]
#这里用ta_record["fieldz"]作为输出文件是有好处
#的,如果fieldz有多个可能的值,例如1,3,5
#那么输出的结果就会生成文件名是1,3,5的三
#个文件,分别记录了fieldz的值为1,3,5的结果,文
#件名是动态产生的,当然您也可以写一个固定的
#文件名把所有结果输出到#一个结果文件中。
}
}
#最后,在程序运行完成时输出当前的系统时间,这样就可以大概算出这个
#程序的总运行时间了。
END { print "\n","Processing ends at: ",strftime("%Y%m%d%H%M%S", systime()) }
|
为什么使用 AWK 来解决这个问题呢?为什么使用AWK处理这些数据呢,首先因为 AWK 是高效的文本处理工具,而且在几乎所有的 Unix/Linux 中都存在, 另外 AWK 十分高效,对文本数据的处理极具针对性先对于其它语言有很多方便的特性。
程序运行后得到了下面这样的结果:
1
2
3
| 11|991|1540|13579210134|00992927770589|00992927770589|
33|996|77|13565078660|95533|099195533|
3|991|30|9918814897|200|09063353941|
|
现在你需要的数据提取出来了,数据怎么使用取决于你的需要,我用 Oracle 数据库的 SQL Loader 工具把这些数据导入到一个临时表中,然后通过 SQL 操作处理剩下的工作。附件中有真实的数据和和完整的代码清单。
运行环境- Red Hat Enterprise Linux AS release 3 (Taroon) (Kernel 2.4.21-4.ELsmp)
- GNU Awk 3.1.1
|