編寫 Linux Shell 腳本程序不要僅限于完成基本的程序功能,認真的分析 Shell 腳本并找出優(yōu)化的方法對個人能力的提高以及對腳本程序的質(zhì)量改善都有重要的意義,希望讀者能從本文中獲得許多實用的 Shell 程序方法。
本文 Shell 程序運行環(huán)境:
程序運行環(huán)境 Redhat Linux As3
GNU bash, version 2.05b.0(1)-release (i386-redhat-linux-gnu)
代碼清單:shellcode.txt
問題描述:有一個普通的通話話單文件(包括"計費號碼","主叫號碼","被叫號碼","開始時間","結(jié)束時間","時長","費用"等其它字段),要求根據(jù)另外一個號段配置文件(由"號段下限"和"號段上限"兩個字段組成)將此話單文件進行分揀過慮。
分揀規(guī)則:如果通話話單文件中的"計費號碼"位于號段文件的某個號段內(nèi),則將此條記錄計入結(jié)果文件 1,否則計入結(jié)果文件 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 成立,所以應(yīng)該將通話話單文件的條記錄計入結(jié)果文件 1 中;對于通話話單文件中的第二條記錄的"計費號碼"為 9926645208 它不屬于號段文件中的任何一個段,所以應(yīng)該將通話話單的第二條記錄計入結(jié)果文件 2 中。
對于這樣一個簡單的問題首先想到的解決方法為:
解決方法1:
寫一個雙重循環(huán),外層循環(huán)為逐條讀取"通話話單文件"并獲取每條記錄的個字段的值"計費號碼",內(nèi)層循環(huán):根據(jù)外層循環(huán)獲得的"計費號碼"在"號段文件"中循環(huán)比較,判斷此號碼是否屬于相應(yīng)號段。
程序代碼如下(省略了文件存在性判斷等語句):
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 ]
#判斷"計費號碼"是否在此號段內(nèi)
then
echo "${f}" >> ./resultfile1.cdr #如果在此號段內(nèi),將此記錄計入結(jié)果文件1中
else
echo "${f}" >> ./resultfile2.cdr #如果不在此號段內(nèi),將此記錄計入結(jié)果文件2中
fi
done < ./numseg.txt
done < ./rttest.txt
解決方法1:對于號段文件和通話話單的記錄數(shù)都比較少的情況下基本可以完成工作,但是當(dāng)兩個文件的記錄數(shù)較多(例如號段文件>50條,話單文件> 10000條)的時候,這種方法就會花費幾個小時甚至幾天的時間才能得出處理結(jié)果。此腳本程序執(zhí)行慢的原因是對第二個循環(huán)內(nèi)的比較運算只用了簡單的順序比較方法,所以當(dāng)號段文件的記錄增多的時候,腳本的執(zhí)行速度會急劇下降。
解決方法2:
將內(nèi)層循環(huán)的逐個比較的方法改為二分查找法進行判斷,程序代碼如下:
#!/bin/bash
#Author Xsh date:08-15-2006
#程序中使用了二分查找法進行號碼的范圍判斷
#為突出重點,省略了文件存在性判斷和異常捕獲以及幫助提示等程序語句
#程序的工作目錄為當(dāng)前目錄
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}) #將下限號段存入數(shù)組arr_lownug
arr_topnug=(${tmptop}) #將上限號段存入數(shù)組arr_topnug
#定義函數(shù)checknum(),輸入?yún)?shù)為需要檢查的"計費號碼",輸出參數(shù)為0或者1
#若checknum()輸出為0 表示"計費號碼" 不在號段文件的所有號段范圍內(nèi)
#若checknum()輸出為1 表示"計費號碼" 在號段文件的所有號段范圍內(nèi)
# checknum()函數(shù)中用二分搜索法進行號碼的判斷
checknum(){
thisnum=$1
ckresult=0
lowflag=0
topflag=$(expr ${#arr_lownug[*]} - 1 ) #標(biāo)注1
MaxIndex=$(expr ${#arr_topnug[*]} - 1 ) #標(biāo)注2
midflag=0
midflag=$(expr ${topflag} / 2 ) #標(biāo)注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 ) #標(biāo)注4
elif["$thisnum"\<"${arr_lownug[${midflag}]}" -o "$thisnum" == \
"${arr_lownug[${midflag}]}" ]
then
topflag=${midflag}
midflag=$(expr `expr ${topflag} + ${lowflag}` / 2 ) #標(biāo)注5
else
echo "Error!"
fi
done
if [ "$thisnum" \< "${arr_topnug[${lowflag}]}" -o "$thisnum" == \
"${arr_topnug[${lowflag}]}" ]
then
return 1
else
return 0
fi
fi
}#函數(shù)定義完畢
while read f
do
org="$(expr substr ${f} 1 10)" #標(biāo)注6
checknum ${org}
returnval=$?
if [ "$returnval" == "1" ]
then
echo "${f}" >> ./Match_result.cdr #將匹配的記錄存入結(jié)果文件1
else
echo "${f}" >> ./NoMatch_result.cdr #將不匹配的記錄存入結(jié)果文件2
fi
done < ./rttest.txt
echo "Time:$(date) ==> Proccess is end! "
exit 0;
將以上程序投入運行,號段文件有 71行記錄,需要分揀的通話話單文件17 萬條左右通過觀察發(fā)現(xiàn)此方法的執(zhí)行效率確實比解決方法1 提高了很多,但是仍需要數(shù)小時的時間才能執(zhí)行完畢。我另外寫了一個同樣功能的C語言程序,執(zhí)行同樣的測試數(shù)據(jù)得出結(jié)果僅需要10~15秒的時間!shell 程序確實要比同樣功能的C程序慢一些,但是目前的情況是用C程序處理只需要幾秒鐘時間, 而Shell程序確需要數(shù)小時!在用此Shell程序處理的時候我用Top命令看了一些系統(tǒng)資源的消耗發(fā)現(xiàn)CPU、內(nèi)存以及IO的占用都極小,說明系統(tǒng)資源很充足,不是系統(tǒng)的問題造成了程序處理速度慢。問題出在哪了呢?
我用命名ps -ef 觀察系統(tǒng)進程的運行情況,發(fā)現(xiàn)每過幾秒種就會有一個expr進程產(chǎn)生,這個進程運行很短的時間就消失了(不仔細觀察可能都看不到有expr這個進程產(chǎn)生)。這時候我覺得我好像發(fā)現(xiàn)程序運行慢的原因了:程序的幾個循環(huán)里面都有用expr進行計算的語句,expr屬于Shell外部命令,所以每次運算都要產(chǎn)生一個 expr進程,而程序的運行種消耗時間的除了IO操作外就數(shù)產(chǎn)生新進程操作了,于是我決定把用expr進行計算的地方都盡量修改為用shell的內(nèi)部命令進行計算。程序變成了下面的樣子(標(biāo)注1~標(biāo)注6為修改過的地方):
程序代碼如下:
―――――――――――――――――――――――――――――――――――――――
#!/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 #循環(huán)讀取號段文件下限號段存入變量tmplow,上限號段存入變量tmptop
arr_lownug=(${tmplow}) #將下限號段存入數(shù)組arr_lownug
arr_topnug=(${tmptop}) #將上限號段存入數(shù)組arr_topnug
#定義函數(shù)checknum(),輸入?yún)?shù)為需要檢查的"計費號碼",輸出參數(shù)為0或者1
#函數(shù)checknum()輸出為0 表示"計費號碼" 不在號段文件的所有號段范圍內(nèi)
#函數(shù)checknum()輸出為1 表示"計費號碼" 在號段文件的所有號段范圍內(nèi)
#函數(shù)checknum()中用二分搜索法進行號碼的判斷
checknum(){ #函數(shù)定義開始
thisnum=$1
ckresult=0
lowflag=0
topflag=$((${#arr_lownug[*]} - 1 )) #標(biāo)注1
MaxIndex=$((${#arr_topnug[*]} - 1 )) #標(biāo)注2
midflag=0
midflag=$((${topflag} / 2 )) #標(biāo)注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=$(( $((${topflag} + ${lowflag})) / 2 )) #標(biāo)注4
elif["$thisnum"\<"${arr_lownug[${midflag}]}" -o "$thisnum" == \
"${arr_lownug[${midflag}]}"]
then
topflag=${midflag}
midflag=$(($((${topflag} + ${lowflag})) / 2 )) #標(biāo)注5
else
echo "Error!"
fi
done
if ["$thisnum" \< "${arr_topnug[${lowflag}]}" -o "$thisnum" == \
"${arr_topnug[${lowflag}]}"]
then
return 1
else
return 0
fi
fi
}#函數(shù)定義結(jié)束
while read f
do
org="${f:0:10}" #標(biāo)注6
checknum ${org}
returnval=$?
if [ "$returnval" == "1" ]
then
echo "${f}" >> ./Match_result.cdr #將匹配的記錄存入結(jié)果文件1
else
echo "${f}" >> ./NoMatch_result.cdr #將不匹配的記錄存入結(jié)果文件2
fi
done < ./rttest.txt
echo "Time:$(date) ==> Proccess is end! "
exit 0;
將修改過的程序進行運行,很快就得出了結(jié)果,總的運行時間沒有超過8分鐘。此時這個程序的運行效率已經(jīng)基本可以讓人接受了。同樣的一個問題由于改進了程序算法和充分利用了LinuxShell的內(nèi)置運算符,將程序的運行時間大大的縮短。當(dāng)然此程序還有可以改進的地方,對此文感興趣的讀者可以對此程序做進一步優(yōu)化。
一個Shell 程序的性能優(yōu)化
更新時間: 2007-06-05 10:10:27來源: 粵嵌教育瀏覽量:1591
免費預(yù)約試聽課
- 姓名
- 電話
- 獲取試聽資格
推薦閱讀
- ·Linux字符設(shè)備驅(qū)動框架解析:file_operations的核心作用與實現(xiàn)
- ·廣東朝歌數(shù)碼科技股份有限公司專場招聘會
- ·深化產(chǎn)教融合,共筑技能人才培養(yǎng)新生態(tài) —— 廣州華立學(xué)院到訪粵嵌從化校區(qū)為深化產(chǎn)教
- ·校企合作新突破 | 粵嵌科技與三亞學(xué)院共探產(chǎn)教融合新路徑
- ·粵嵌科技入選國家級職業(yè)數(shù)字展館聯(lián)合建設(shè)單位,賦能計算機程序設(shè)計員高技能人才培養(yǎng)
- ·嵌入式實時操作系統(tǒng)的性能優(yōu)化與實現(xiàn)路徑
- ·校企攜手賦能教育!粵嵌科技助力海南科技職業(yè)大學(xué)探索 AGI 時代教學(xué)新范式
- ·嵌入式系統(tǒng)中的低功耗設(shè)計策略與實現(xiàn)路徑
- ·深圳市軒宇軟件開發(fā)有限公司專場招聘會
- ·嵌入式系統(tǒng)中的代碼空間優(yōu)化:策略與實踐