勿(误)入逐行处理怪圈,关键字段落(shell,awk)

今天有个请求,想知道AIX TL升级后fileset的版本升级信息,就是从什么升到了什么。
AIX方面有个方法去查看这个升级历史,就是lslpp -h,结果加工过后如下(后来发现做了多余的加工)

filesetA
        1.0 2/13/77
        1.1 29/2/00
        1.2 8/8/88
filesetB
        1.0 2/13/77
        1.1 29/2/00
        1.2 8/8/89
filesetC
        1.0 2/13/77
        1.1 29/2/00
        1.2 8/8/88
filesetD
        1.0 2/13/77
        1.1 29/2/00
        1.2 8/8/89
filesetCA
        1.0 2/13/77
        1.1 29/2/00
        1.2 8/8/88
filesetCB
        1.0 2/13/77
        1.1 29/2/00
        1.2 8/8/88
filesetCD
        1.0 2/13/77
        1.1 29/2/00
        1.2 8/8/89
filesetC9
        1.0 2/13/77
        1.1 29/2/00
        1.2 8/8/88
filesetDE
        1.0 2/13/77
        1.1 29/2/00
        1.2 8/8/88
filesetZP
        1.0 2/13/77
        1.1 29/2/00
        1.2 8/8/89
filesetCW
        1.0 2/13/77
        1.1 29/2/00
        1.2 8/8/88
filesetCX
        1.0 2/13/77
        1.1 29/2/00
        1.2 8/8/88
filesetMQ
        1.0 2/13/77
        1.1 29/2/00
        1.2 8/8/89
filesetPO
        1.0 2/13/77
        1.1 29/2/00
        1.2 8/8/89
filesetYT
        1.0 2/13/77
        1.1 29/2/00
        1.2 8/8/88
filesetV
        1.0 2/13/77
        1.1 29/2/00
        1.2 8/8/88
filesetX
        1.0 2/13/77
        1.1 29/2/00
        1.2 8/8/88

我要找出8/8/89这个日期的段落,然后版本升级前后来个可读性增强
例如
filesetB 1.1->1.2

花了两个小时,之前一小时花费在如何逐行判断哪个是标题,哪个是版本信息的怪圈里
然后发现这样的逻辑分支太多了,简直是自找麻烦
举个例子,我就懒得写了,写个大概

cat filename|while read LINE
do
if 标题的正则 $LINE;then
清空临时文件(cat /dev/null>tempfile)
titlename=“巴拉巴拉”
else
if 版本信息为空;then
versioninfo=“巴拉巴拉”
else
versioninfo="$versioninfo换行符$versioninfo"
fi
打印信息到临时文件
unset 利用的变量(要判断是否已有变量)
fi
done

所以就放弃了,之前弄过一个段落截取的函数,思维就开阔了,即把标题的行数装入数组,通过数组之间的元素弄出每个段落

[vagrant@amainst perltest]$ fileset_ck () {
> filename=$1
> keyword=$2
> list=(`grep -n ^[a-zA-Z] $filename|awk -F':' '{print $1}'`)
> i=0
> endnum=`echo ${#list[@]}`
> ubound=`expr $endnum - 1`
> while [ $i -le $ubound ]
> do
> if [ $i -eq $ubound ];then
> sed -n "${list[$i]},$"p $filename>tempfile
> else
> nextline=`expr $i + 1`
> sed -n "${list[$i]},${list[$nextline]}"p $filename|head -n -1>tempfile
> fi
> if grep -q $keyword tempfile;then
> filesetname=`head -1 tempfile`
> oldversion=`tail -2 tempfile|head -n -1|awk '{print $1}'`
> newversion=`tail -1 tempfile|awk '{print $1}'`
> echo -e "$filesetname\t$oldversion->$newversion"
> fi
> i=`expr $i + 1`
> done
> unset filename keyword listi filesetname oldversion newversion
> }
[vagrant@amainst perltest]$ time fileset_ck testfile 89
filesetB        1.1->1.2
filesetD        1.1->1.2
filesetCD       1.1->1.2
filesetZP       1.1->1.2
filesetMQ       1.1->1.2
filesetPO       1.1->1.2

real    0m0.117s
user    0m0.059s
sys     0m0.056s

看到这里请注意!!!!!!以上都是在浪费时间,再经过冷静思考以后我想起aix的grep是有关键字段落的参数的,那就grep -p 关键字
但是我现在用的是centos来写记录这些体验,centos(一切linux发行版)怎么弄段落呢?
那就是awk中对于RS这个保留字的理解,RS是记录的分隔符。
以下是命令

以下命令有点长
首先管道sed把首字符为英文的行前插入两个空行,这样好处理段落(其实数据本来就是段落,我还特意把空行删掉了)
然后awk BEGIN语句里对awk进行一个记录分隔符的设定(RS="\n\n")就是两个空行前的数据为一个记录
[vagrant@amainst perltest]$ time cat testfile|sed '/^[a-zA-Z]/i\\n'|awk 'BEGIN{RS="\n\n"}
> /89/{sub("\n","",$0)#这里匹配关键字89,因为我要找的数据是这个,其实不够严谨,但目前数据源没有多余的数据干扰。
> ubound=split($0,info,"\n")#变量=split这种方法之前的文章有提到,就是找出split出来的数组的长度。
> print info[1]"\t"info[ubound-1]" -> "info[ubound]}'|awk 'BEGIN{OFS="\t"}{NF=NF;$NF=$(NF-3)="";print}'
#最后一个管道是因为懒得在前一个awk管道处理了,就放到这里来
filesetB        1.1             ->      1.2
filesetD        1.1             ->      1.2
filesetCD       1.1             ->      1.2
filesetZP       1.1             ->      1.2
filesetMQ       1.1             ->      1.2
filesetPO       1.1             ->      1.2

real    0m0.005s#主要看这里的视角消耗,比前一个shell快多了
user    0m0.004s
sys     0m0.001s

为什么awk的方法要比shell快

对于性能的问题,最好都去研究一下时间复杂度的问题。(老实说我自己也解释不清楚)
以下是初浅理解

[vagrant@amainst perltest]$ echo ${#list[@]}
17
shell数组的长度是17,时间复杂度来看,里面只有一个循环,应该是O(n),即O(17)
[vagrant@amainst perltest]$ cat testfile|sed '/^[a-zA-Z]/i\\n'|awk 'BEGIN{RS=OFS="\n\n"}{print NR}'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
awk的记录比shell还多一个,哈哈,即O(18)
[vagrant@amainst perltest]$ cat testfile|sed '/^[a-zA-Z]/i\\n'|awk 'BEGIN{RS=OFS="\n\n"}/89/{print NR}'
3
5
8
11
14
15
但是匹配关键字后记录只有6,即O(6),那是不是快在这里呢?

花了一天时间,不得不说shell写法不仅耗时久(写的也久,语法的符号用多了还容易出语法错误)。

推荐如果会awk,多花时间学习,书写效率和执行效率都要比shell快。

最后,用shell也千万不要用逐行循环

[vagrant@amainst perltest]$ wc -l testfile
68 testfile

以上,程序要处理68行数据,即O(68),执行效率可想而知。

你可能感兴趣的:(勿(误)入逐行处理怪圈,关键字段落(shell,awk))