shell本身是不能實現多線程的,但是可以通過啟動子進程,並將子進程放入後台執行來類比多線程,為了在提高指令碼執行效率的同時又不明顯增加負載的作用,還需要對同時放入背景進程數做下限制。
代碼如下 |
複製代碼 |
#!/bin/bash set -x # 開啟偵錯模式 #判斷是否有參數 if [ $# != 1 ];then echo "您輸入的參數有誤" exit -1 fi # 允許的最大進程數 MAX_THREAD_NUM=5 tmp_fifo_file=/tmp/$$.fifo # 以指令碼啟動並執行當前進程ID號作為檔案名稱 mkfifo "$tmp_fifo_file" # 建立一個隨機fifo管道檔案 exec 9<>"$tmp_fifo_file" # 定義檔案描述符9指向這個fifo管道檔案 rm "$tmp_fifo_file" # 預先寫入指定數量的分行符號到fifo管道檔案中,一個分行符號代表一個進程 for((i=0;i<$MAX_THREAD_NUM;i++));do echo done >&9 # 迴圈讀出url並判斷狀態代碼 while read line do { # 進程式控制制 read -u 9 # 從檔案描述符9中讀取行,實際指向fifo管道 { isok=`curl -I -L -m 60 -o /dev/null -s -w %{http_code} $line` if [ "$isok" = "200" ];then echo $line "OK" else echo $line $isok fi echo >&9 # ,當前進程結束,往fifo管道檔案中寫入一個空行 }& } done < $1 wait echo '執行結束' exec 9>&- # 刪除檔案描述符9 exit 0 |
指令碼的任務是對一個url列表中的網址進行判斷,判斷這些網址是否可以繼續訪問,具體方法是,通過curl擷取http的狀態代碼來判斷。
上面紅色部分{}中的語句被放進子進程中在後台執行,當fifo中5個空行讀完後,迴圈繼續等待 read 中讀取fifo資料,當背景子進程完成任務後,排隊往fifo輸入空行,這樣fifo中又有了資料,迴圈繼續執行。
下面看看shell執行的結果
代碼如下 |
複製代碼 |
# bash scanUrl.sh url.txt + '[' 1 '!=' 1 ']' + MAX_THREAD_NUM=5 + tmp_fifo_file=/tmp/111cn.net + mkfifo /tmp/111cn.net + exec + rm /tmp/111cn.net + (( i=0 )) + (( i<5 )) + echo + (( i++ )) + (( i<5 )) + echo + (( i++ )) + (( i<5 )) + echo + (( i++ )) + (( i<5 )) + echo + (( i++ )) + (( i<5 )) + echo + (( i++ )) + (( i<5 )) + read line + read -u 9 ++ curl -I -L -m 60 -o /dev/null -s -w '%{http_code}' http://111cn.net / + read line + read -u 9 ++ curl -I -L -m 60 -o /dev/null -s -w '%{http_code}' http://111cn.net / + read line + read -u 9 ++ curl -I -L -m 60 -o /dev/null -s -w '%{http_code}' http://111cn.net / + read line + read -u 9 ++ curl -I -L -m 60 -o /dev/null -s -w '%{http_code}' http://111cn.net / + read line + read -u 9 ++ curl -I -L -m 60 -o /dev/null -s -w '%{http_code}' http://111cn.net / + read line + read -u 9 # fifo檔案中的5個空行讀完了,等待其它子進程寫入fifo + isok=200 + '[' 200 = 200 ']' + echo http://111cn.net / OK http://111cn.net / OK + echo # 這個子進程完成任務,寫入fifo一個空行,啟動一個子進程 ++ curl -I -L -m 60 -o /dev/null -s -w '%{http_code}' http://111cn.net / + read line + read -u 9 + isok=200 + '[' 200 = 200 ']' + echo http://50vip.com/ OK http://50vip.com/ OK + echo ++ curl -I -L -m 60 -o /dev/null -s -w '%{http_code}' http://111cn.net /info/ + read line + read -u 9 + isok=200 + '[' 200 = 200 ']' + echo http://361a.net/ OK http://361a.net/ OK + echo ++ curl -I -L -m 60 -o /dev/null -s -w '%{http_code}' http://111cn.net / + read line + wait # 輸入檔案中的url都已經處理完成或在子進程中處理,等待所有子進程結束 + isok=200 + '[' 200 = 200 ']' + echo http://111cn.net / OK http://111cn.net / OK + echo + isok=000 + '[' 000 = 200 ']' + echo http://5imovie.org/ 000 http://5imovie.org/ 000 + echo + isok=200 + '[' 200 = 200 ']' + echo http://111cn.net / OK http://52ixwebhosting.com/ OK + echo + isok=404 + '[' 404 = 200 ']' + echo http://111cn.net /info/ 404 http://111cn.net /info/ 404 + echo + isok=000 + '[' 000 = 200 ']' + echo http://111cn.net / 000 http://42.hcocoa.com/ 000 + echo + echo $'346211247350241214347273223346235237' 執行結束 + exec + exit 0 |
下面我們再來看個例子
代碼如下 |
複製代碼 |
#!/bin/bash function pinghost { ping $1 -c 1 -w 10 |grep rtt|cut -d “/” -f6 } tmp_fifofile=”/tmp/$.fifo” # 指令碼啟動並執行當前進程ID號作為檔案名稱 mkfifo $tmp_fifofile # 建立一個隨機fifo管道檔案 exec 6<>$tmp_fifofile # 定義檔案描述符6指向這個fifo管道檔案 rm $tmp_fifofile thread=10 for ((i=0;i<$thread;i++));do # for迴圈 往 fifo管道檔案中寫入10個空行 echo done >&6 while read domain do read -u6 # 從檔案描述符6中讀取行(實際指向fifo管道) { pinghost ${domain}; # 執行pinghost函數 echo >&6 # 再次往fifo管道檔案中寫入一個空行。 }& # 放到後台執行 done</home/miotour/ip.txt wait #因為之前的進程都是後台執行,因此要有wait來等待所有的進程都執行完畢後才算整個指令碼跑完。 exec 6>&- #刪除檔案描述符6 exit 0
|
說明:{} 這部分語句被放入後台作為一個子進程執行,這部分幾乎是同時完成的,當fifo中10個空行讀完後 while迴圈
繼續等待 read 中讀取fifo資料,當背景10個子進程後,按次序排隊往fifo輸入空行,這樣fifo中又有了資料,for語句繼續執行。