假设我们有一个数据文件形如下:
A,B
A,C
A,D
B,E
B,F
D,A
D,B
E,G
....
需求是要将其转变为如下形式:
A:B,C,D
B:E,F
D:A,B
E,G
......
需求很简单,循环遍历文件,然后trace第一列即可。
在这里,我们讨论用Shell脚本如何实现这个简单的需求。
在shell中面对这样的需求,最好的办法莫过于使用awk了,不过除了awk之外,linux也有其他命令能遍历文件内容,比如说cat以及输入重定向等,下面我们就对这几种文件遍历方法做下比较。
首先申明一点,关于文件遍历过程中,如果得到目标结果的逻辑是一样的,说白了就是每读入一行,检查第一列而已。
1. awk方法:
BEGIN{ FS="," curmember=""; } { if(curmember==""){ curmember=$1; offer=$2; } else if(curmember != $1){ print curmember":"offer curmember=$1; offer=$2; } else{ offer=offer","$2; } } END{ print curmember":"offer; }
2. forIn+cat方法:
#!/bin/bash curmember=""; offer=""; #cat orderinfo | while read line for line in `cat orderinfo` do tmpmember=${line%%,*}; if [ "$curmember" == "" ]; then curmember=$tmpmember offer=${line##*,} elif [ "$tmpmember" != "$curmember" ]; then echo ${curmember}:${offer} curmember=${tmpmember} offer=${line##*,} else offer=${offer},${line##*,} fi done echo ${curmember}:${offer}
3. 文件描述符打开文件
#!/bin/bash curmember=""; offer=""; exec 4<orderinfo while read line<&4 do tmpmember=${line%%,*}; if [ "$curmember" == "" ]; then curmember=$tmpmember offer=${line##*,} elif [ "$tmpmember" != "$curmember" ]; then echo ${curmember}:${offer} curmember=${tmpmember} offer=${line##*,} else offer=${offer},${line##*,} fi done exec 4<&- echo ${curmember}:${offer}
4. while循环中直接输入重定向
#!/bin/bash curmember=""; offer=""; while read line do tmpmember=${line%%,*}; if [ "$curmember" == "" ]; then curmember=$tmpmember offer=${line##*,} elif [ "$tmpmember" != "$curmember" ]; then echo ${curmember}:${offer} curmember=${tmpmember} offer=${line##*,} else offer=${offer},${line##*,} fi done<orderinfo echo ${curmember}:${offer}
四种方法的执行效率如下,文件行数69000+
awk : 185 ms
forIn : 7187 ms
exec : 7931 ms
while : 7708 ms
上述结果中awk速度是最快的,其他三个虽然各有快慢,但都差不多,而且都比awk要慢很多。
这也印证了awk在流式处理文件中的先天优势,同时其代码也是最简单的,屏蔽了文件操作的相关内容。