bash更改行列順序

目地:由於修改asterisk設定檔內的路由順序的次數很頻繁,因此就寫了一個小程式來協助完成此工作

asterisk 的設定檔如下:

[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行去了,其它行則不動。

[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本身強大的功能,透過一些複雜的參數,實現了兩行資料直接對換的功能。此版本得以實現是完全參照此頁所寫出來的。

#!/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功能實現:

#!/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 對於程式的語法要求是很嚴格的,差一點都不行的。

分類: bash, voip。這篇內容的永久連結

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *