目地:由於修改asterisk設定檔內的路由順序的次數很頻繁,因此就寫了一個小程式來協助完成此工作
asterisk 的設定檔如下:
檢視程式碼 BASH
[outbound-route] exten => s,1,Noop() ;設定動態功能鍵 Set(DYNAMIC_FEATURES=vicGroup) exten => s,n,set(_MainRoomNum=${CALLERID(number)}) same => n,Gosub(設定來電顯示,cell${RAND(4,9)},1) same => n,Dial(SIP/路由1/235${x},32,gCX) same => n,Dial(SIP/路由2/7852${x},32,gCX) same => n,Dial(SIP/路由3/9957${x},32,gCX) same => n,Dial(SIP/路由4/9967${x},32,gCX) same => n,Dial(SIP/路由5/9867${x},32,gCX) same => n,Return |
希望透過使用者按簡單的數字來改變路由順序,而不改動到其它的字。
比如說:當我按下3之後,內容會變成這樣,路由3跑到第1行去了,其它行則不動。
檢視程式碼 BASH
[outbound-route] exten => s,1,Noop() ;設定動態功能鍵 Set(DYNAMIC_FEATURES=vicGroup) exten => s,n,set(_MainRoomNum=${CALLERID(number)}) same => n,Gosub(設定來電顯示,cell${RAND(4,9)},1) same => n,Dial(SIP/路由3/9957${x},32,gCX) same => n,Dial(SIP/路由1/235${x},32,gCX) same => n,Dial(SIP/路由2/7852${x},32,gCX) same => n,Dial(SIP/路由4/9967${x},32,gCX) same => n,Dial(SIP/路由5/9867${x},32,gCX) same => n,Return |
需注意的是,此設定檔內有許多特殊符號,如大於、等號、空格…等,因此會使得變數在處理上變得很麻煩、困難重重。
針對以上需求,寫出了兩個版本的shell script。
第1個版本,使用sed本身強大的功能,透過一些複雜的參數,實現了兩行資料直接對換的功能。此版本得以實現是完全參照此頁所寫出來的。
檢視程式碼 BASH
#!/bin/bash file="outbound.conf" old=$IFS IFS=' ' #此行將檔案內容存入變數 content=$(cat $file) #計算有 Dial 這樣的字串有幾行 length=$(echo $content | grep -o 'Dial' | wc -l) #計算 Dial 這樣的字串 第一次出現時是位於第幾行 mainroute=$(echo $content | grep -n 'Dial' | head -n1 | cut -d ':' -f1) headlength=$(( $mainroute -1 )) IFS=$old #echo $length echo "目前路由順序如下:" echo "$content" |grep Dial |nl #grep Dial $file|nl -v0 echo " 參照上方的路由表輸入你要設定主要路由的號碼" read -p " 請輸入數字(1,2,3,4...):" num echo "" if [ "$mainroute" -eq "$(($num+$headlength))" ]; then echo "已經是主要路由了" exit 0 fi echo " " echo "底下是修改後的路由表" #sed -n "$mainroute! {p;d};:1;$(($num+$headlength-1))! {N;b1};h;n;p;g;p" $file sed -ni "$mainroute! {p;d};:1;$(($num+$headlength-1))! {N;b1};h;n;p;g;p" $file grep Dial $file|nl /usr/sbin/asterisk -r -x "reload" echo "路由變更完成" #line=3 #cat bound.conf | sed -n "6! {p;d};:1;$(($line+5-1))! {N;b1};h;n;p;g;p" #說明: 此指令為兩行資料互相對調, 6! 代表你要修改的那一行 $line 代表另一行 |
第2個版本,主要使用陣列,加上簡單的sed功能實現:
檢視程式碼 BASH
#!/bin/bash IFS=$'\n' #由於我們所使用的變數內容內含空格 如:same => n,Dial(SIP/路由/5131${x},32,gCX) #這樣在我們將此變數塞入陣列時容易出錯,因為空格的關係,此變數會被塞進3個陣列元素之中 #所以我們要更改 IFS 預設的分隔符號為換行符號 $'\n\ 這樣它才能將整行(內含空格)的變數塞入同一個陣列元素之中 #IFS 中的第一個預設字符來作為每個位置參數之間的分隔符號,而這個預設字符通常為空白字元 #參考資料:http://www.suse.url.tw/sles10/lesson10.htm #定義變數 file1=./outbound.conf #此行將檔案內容存入變數 content=$(cat $file1) last=(`tail -n1 $file1`) #計算 Dial 這樣的字串 第一次出現時是位於第幾行 #mainroute=$(cat $file1 | grep -n 'Dial' | head -n1 | cut -d ':' -f1) echo "目前路由順序如下:" echo "$content" | grep Dial |nl -v0 #宣告陣列 route1,並將路由分別資料存入陣列之中 declare -a route1=(`echo "$content" | grep Dial`) declare -a route2 read -p "請輸入數字(1,2,3...):" keyin #將使用者所選的路由存到陣列 route2 裡的第0個元素去 route2[0]=${route1[$keyin]} #將陣列 route1 裡的資料分別存入 陣列 route2 #特別注意的是 route2[0] 裡面的資料剛剛已經存入了使用者所選擇的路由,所以要從 route2[1]開始存放 #即按 route1[0]=route2[1] 這樣的方式存入 #而陣列 route1[*] 裡的資料又不能全存入,得剔掉使用者所選的那個路由,是變數 $keyin for (( i=0; i<=${#route1[@]}-1; i=i+1 )) do if [ $i != $keyin ]; then route2[$i+1]=${route1[$i]} fi done #echo print array 所有內容 #echo "${route2[*]}" #echo 計算 array 總數 #echo "${#route2[@]}" #echo print 陣列元素 2 #echo "${route2[2]}" #刪除從 Dial 這行開始後面的所有資料 sed -i '/Dial/,$d' $file1 #底下是使用另一種刪除法,從主要路由哪一行開始,刪除後面所有資料 #sed -i ''$mainroute',$d' $file1 #sed 注意事項: 若要在 sed 內使用變數,請在變數左右加單引號 #開始注入新的路由資料,即 route2[*] 的內容倒回原設定檔內 for item in ${route2[*]} do echo $item >> $file1 done #注入最後一行資料 echo $last >> $file1 #顯示修改結果 echo "" echo "路由更新結果如下:" grep Dial $file1 |nl -v0 /usr/sbin/asterisk -r -x "reload" echo "路由變更完成" |
程式設計的思惟:
第1步將路由資料存入陣列route1之中
第2步讓使用者選擇某一個路由,再將使用者所選的路由存入陣列route2的第0個元素之中。
第3步將route1陣列裡的資料,存入route2陣列中
第4步刪除原設定檔內的路由資料,開始注入route2的內容
第5步reload asterisk
此程式最困難的地方在於變數的處理,由於路由資料那一行內含有一些特殊符號,使得路由資料轉為變數存入陣列時有很多問題要克服,而好不容易存入陣列中了,要讀出來的時候,所有的陣列元素全部變成只有一行輸出,而不是我想要的一個路由資料就是一行,因此在解決這些問題上面花了不少時間,這也才明白了 bash 對於程式的語法要求是很嚴格的,差一點都不行的。