编写 Linux Shell 脚本程式不要仅限于完成基本的程式功能,认真的分析 Shell 脚本并找出优化的方法对个人能力的提高连同对脚本程式的质量改善都有重要的意义,希望读者能从本文中获得许多实用的 Shell 程式方法。

本文 Shell 程式运行环境:

  • 程式运行环境 Redhat Linux As3
  • GNU bash, version 2.05b.0(1)-release (i386-redhat-linux-gnu)
  • 代码清单:shellcode.txt

问题描述:有一个普通的通话话单文档(包括"计费号码","主叫号码","被叫号码","开始时间","结束时间","时长","费用"等其他字段),需要根据另外一个号段配置文档(由"号段下限"和"号段上限"两个字段组成)将此话单文档进行分拣过虑。

分拣规则:假如通话话单文档中的"计费号码"位于号段文档的某个号段内,则将此条记录计入结果文档 1,否则计入结果文档 2。

通话话单文档样例:


9013320003|9013320003|9918128025|20060814163420|20060814163450|30|20|00|01|005

            9926645208|9926645208|9918188065|20060814163415|20060814163545|90|30|00|01|005

            9934877207|9934877207|9936972003|20060814163620|20060814163930|190|50|00|01|005

            ......

            ......

            

号段配置文档样例:


9013305000,9013327999

            9013767000,9013768999

            9923670000,9923679999

            9928998000,9928999999

            9932310000,9932319999

            9932333400,9932333599

            9936034000,9936036999

            9936084000,9936084999

            9998537000,9998537999

            9998620000,9998629999

            9998690000,9998699999

            

例如:

对于通话话单文档的第一条记录中的"计费号码"为 9013320000,此号码正好属于号段配置文档的第一个号段 9013305000,9013327999中,即:条件 9013305000<= 9013320000 <=9013327999 成立,所以应该将通话话单文档的第一条记录计入结果文档 1 中;对于通话话单文档中的第二条记录的"计费号码"为 9926645208 他不属于号段文档中的任何一个段,所以应该将通话话单的第二条记录计入结果文档 2 中。

对于这样一个简单的问题首先想到的解决方法为:

解决方法1:

写一个双重循环,外层循环为逐条读取"通话话单文档"并获取每条记录的第一个字段的值"计费号码",内层循环:根据外层循环获得的"计费号码"在"号段文档"中循环比较,判断此号码是否属于相应号段。

程式代码如下(省略了文档存在性判断等语句):


while read f

            do

            org="$(expr substr ${f} 1 10)"   #取得"计费号码"存入变量org中

            while read numseg

            do

            nglow="$(expr substr ${numseg} 1 10 )"   #将号段下限存入变量nglow

            ngtop="$(expr substr ${numseg} 12 10 )"  #将号段上限存入变量ngtop

            if [ "$org" \> "$nglow"  -a "$org" \< $ngtop ]

            #判断"计费号码"是否在此号段内

            then

            echo "${f}" >> ./resultfile1.cdr #假如在此号段内,将此记录计入结果文档1中

            else

            echo "${f}" >> ./resultfile2.cdr #假如不在此号段内,将此记录计入结果文档2中

            fi

            done < ./numseg.txt

            done < ./rttest.txt

            

解决方法1 对于号段文档和通话话单的记录数都比较少的情况下基本能够完成工作,但是当两个文档的记录数较多(例如号段文档>50条,话单文档> 10000条)的时候,这种方法就会花费几个小时甚至几天的时间才能得出处理结果。此脚本程式执行慢的原因是对第二个循环内的比较运算只用了最简单的顺序比较方法,所以当号段文档的记录增多的时候,脚本的执行速度会急剧下降。

解决方法2:

将内层循环的逐个比较的方法改为二分查找法进行判断,程式代码如下:


#!/bin/bash

            #Author Xsh  date:08-15-2006

            #程式中使用了二分查找法进行号码的范围判断

            #为突出重点,省略了文档存在性判断和异常捕获连同帮助提示等程式语句

            #程式的工作目录为当前目录

            echo "Time:$(date)==>Strat to processing........." #显示程式开始运行时间

            while read numseg

            do

            tmplow="${tmplow} $(expr substr ${numseg} 1 10 & >/dev/null ) "

            tmptop="${tmptop} $(expr substr ${numseg} 12 10 & >/dev/null ) "

            done < ./numseg.txt

            #读取号段文档,下限号段存入变量tmplow,上限号段存入变量tmptop

            arr_lownug=(${tmplow}) #将下限号段存入数组arr_lownug

            arr_topnug=(${tmptop})	#将上限号段存入数组arr_topnug

            #定义函数checknum(),输入参数为需要检查的"计费号码",输出参数为0或1

            #若checknum()输出为0 表示"计费号码" 不在号段文档的任何号段范围内

            #若checknum()输出为1 表示"计费号码" 在号段文档的任何号段范围内

            # checknum()函数中用二分搜索法进行号码的判断

            checknum(){

            thisnum=$1

            ckresult=0

            lowflag=0

            topflag=$(expr ${#arr_lownug[*]} - 1 )  #标注1

            MaxIndex=$(expr ${#arr_topnug[*]} - 1 ) #标注2

            midflag=0

            midflag=$(expr ${topflag} / 2 )		#标注3

            if [ "${thisnum}" \< "${arr_lownug[0]}" -o "${thisnum}" \>

            "${arr_topnug[${MaxIndex}]}"  ]

            then

            return 0

            else

            while [ "$lowflag" != "$midflag" ]

            do

            if[ "$thisnum" \> "${arr_lownug[${midflag}]}" -o "$thisnum" ==             "${arr_lownug[${midflag}]}" ]

            then

            lowflag=${midflag}

            midflag=$(expr `expr ${topflag}   ${lowflag}` / 2 ) #标注4

            elif["$thisnum"\<"${arr_lownug[${midflag}]}" -o "$thisnum" ==             "${arr_lownug[${midflag}]}" ]

            then

            topflag=${midflag}

            midflag=$(expr `expr ${topflag}   ${lowflag}` / 2 ) #标注5

            else

            echo "Error!"

            fi

            done

            if [ "$thisnum" \< "${arr_topnug[${lowflag}]}" -o "$thisnum" ==             "${arr_topnug[${lowflag}]}" ]

            then

            return 1

            else

            return 0

            fi

            fi

            }#函数定义完毕

            while read f

            do

            org="$(expr substr ${f} 1 10)" #标注6

            checknum ${org}

            returnval=$?

            if [ "$returnval" == "1"  ]

            then

            echo "${f}" >> ./Match_result.cdr  #将匹配的记录存入结果文档1

            else

            echo "${f}" >> ./NoMatch_result.cdr #将不匹配的记录存入结果文档2

            fi

            done < ./rttest.txt

            echo "Time:$(date) ==> Proccess is end! "

            exit 0;

            

文章整理:西部数码--专业提供域名注册虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!