Shell 都以串行的方式自上而下執行命令,不適用須要大量做業的場景。 學習此篇shell腳本進程併發,可以大大提升工做效率~ 經過wait 和 & 後臺符號 能夠實現並行,但沒法控制進程數。shell
{ task }& done wait
{} 將主執行程序變爲一個塊,使用&放入後臺 wait 函數等待全部後臺進程執行程序,不然繼續執行後續命令直到整個腳本結束bash
建立一個fifo文件, 做爲進程池, 裏面存放必定數目的"令牌".做業運行規則以下: 全部做業排隊依次領取令牌; 每一個做業運行前從進程池中領取一塊令牌, 完成後再歸還令牌; 當進程池中沒有令牌時, 要運行的做業只能等待. 這樣就能保證同時運行的做業數等於令牌數.服務器
管道 = = 》有名管道、無名(匿名)管道 有名管道:mkfilo 建立一個管道文件 有名管道: 「cat 1.file | grep ‘xxx’ 」 「|」 ==》建立一個無名管道,直接做爲兩個進程的數據通道併發
exec 自行定義,綁定文件操做符dom
系統默認三個文件操做符 0、一、2 = = 》 stdin、stdout、stderr ls /proc/self/fdcurl
模板ide
#!/bin/bash trap "exec 6>&-;exec 6<&-;wxit 0" 2 #接受2 程序終止(interrupt)信號 "ctrl c" 後的操做。關閉fd6 tmp_fifofile=/tmp/$$.fifo //$$ 進程pid mkfifo $tmp_fifofile //建立爲進程pid的管道 //我經常使用$((RANDOM)),大機率的避免與已有文件重複 exec 6<>$tmp_fifofile //以6爲文件描述符fd打開管道 <>表明讀寫 rm $tmp_fifofile thread=250 //定義併發進程數量,上文的令牌數量 #在fd6中放入$thread 個空行做爲令牌 for ((i=0; i<=$thread;i++)) do echo done >&6 for i in `` //能夠省略,直接在{}括號內添加執行命令 do read -u6 //read 讀取行,領取令牌 { echo >& 6 //歸還令牌 }& //{ }&放入後臺 done wait //等待全部後臺子進程結束 exec 6>&- //關閉fd6 exec 6<&- //關閉fd6
學術不精。歡迎評論一塊兒討論!~函數
附上一個本身寫的使用併發,檢查大批量站點的域名檢測腳本 將待檢查的腳本放入指定目錄就好了~學習
#!/bin/bash #建立今日目錄 if [ ! -d "./$(date +%y-%m-%d)" ];then mkdir -p /script/$(date +%y-%m-%d) fi dir=/script/$(date +%y-%m-%d) function global() { #第一次curl檢測 tmp_fifofile=/tmp/$(($RANDOM%1000)).fifo mkfifo $tmp_fifofile exec 6<>$tmp_fifofile rm $tmp_fifofile thread=256 for ((i=0; i<=$thread;i++)) do echo done >&6 for ((i=0;i<=$thread;i++)) do echo >&6 done for i in `cat /script/domain/$url` do read -u6 { code=$(curl -o /dev/null --retry 2 --connect-timeout 10 -s -w %{http_code} $i) echo "$code $i" >> $dir/$url-first.log echo >& 6 }& done wait exec 6>&- exec 6<&- grep -v '200\|301\|302' $dir/$url-first.log |tail -n +2 |awk -F' ' '{print $2}' > $dir/$url-second.log rm -f $dir/$url-first.log #第二次wget檢測 tmp_fifofile=/tmp/$(($RANDOM%1000)).fifo mkfifo $tmp_fifofile exec 6<>$tmp_fifofile rm $tmp_fifofile thread=128 for ((i=0; i<=$thread;i++)) do echo >&6 done for i in `cat $dir/$url-second.log` do read -u6 { wget -T 10 --spider -t 2 $i &>/dev/null $i >> /dev/null if [ $? = 0 ];then echo $i >> /dev/null else echo $i >> $dir/$url-third.log fi echo >& 6 }& done wait exec 6>&- exec 6<&- rm -f $dir/$url-second.log #第三次curl檢測 tmp_fifofile=/tmp/$(($RANDOM%1000)).fifo mkfifo $tmp_fifofile exec 6<>$tmp_fifofile rm $tmp_fifofile thread=128 for ((i=0; i<=$thread;i++)) do echo >&6 done for i in `cat $dir/$url-third.log` do read -u6 { code=$(curl -o /dev/null --retry 2 --connect-timeout 10 -s -w %{http_code} $i) echo "$code $i" >> $dir/$url-fourth.log echo >& 6 }& done wait exec 6>&- grep -v '200\|301\|302' $dir/$url-fourth.log |tail -n +2 >> $dir/last.log rm -f $dir/$url-third.log rm -f $dir/$url-fourth.log } function last (){ grep -v '200\|301\|302' $dir/last.log |awk -F' ' '{print $2}' >> $dir/last2.log rm -f $dir/last.log tmp_fifofile=/tmp/last.fifo mkfifo $tmp_fifofile exec 6<>$tmp_fifofile rm $tmp_fifofile thread=64 for ((i=0; i<=$thread;i++)) do echo done >&6 for ((i=0;i<=$thread;i++)) do echo >&6 done for i in `cat $dir/last2.log` do read -u6 { code=$(curl -o /dev/null --retry 2 --connect-timeout 10 -s -w %{http_code} $i) echo "$code $i" >> $dir/last3.log echo >& 6 }& done wait exec 6>&- exec 6<&- rm -f $dir/last2.log echo "請手動複覈如下域名:" > $dir/$(date +%H-00)domain.log grep -v '200\|301\|302' $dir/last3.log >> $dir/$(date +%H-00)domain.log rm -f $dir/last3.log } function main () { tmp_fifofile=/tmp/main.fifo mkfifo $tmp_fifofile exec 8<>$tmp_fifofile rm $tmp_fifofile thread=2 for ((i=0; i<=$thread;i++)) do echo done >&8 for url in `ls -l /script/domain/ | tail -n +2 | awk -F' ' '{print $9}'` do read -u8 { global $url echo >& 8 }& done wait exec 8>&- exec 8<&- } main last mail -s "檢測結果來自xx服務器 :" xxxxxxxx@qq.com < $dir/$(date +%H-00)domain.log