Shell 都以序列的方式自上而下執行命令,不適用需要大量作業的場景。
學習此篇shell指令碼程式併發,能夠大大提高工作效率~
通過wait 和 & 後臺符號 可以實現並行,但無法控制程式數。
{
task
}&
done
wait
{} 將主執行程式變為一個塊,使用&放入後臺
wait 函式等待所有後臺程式執行程式,否則繼續執行後續命令直到整個指令碼結束
通過有名管道控制併發程式數
建立一個fifo檔案, 作為程式池, 裡面存放一定數目的”令牌”.作業執行規則如下: 所有作業排隊依次領取令牌; 每個作業執行前從程式池中領取一塊令牌, 完成後再歸還令牌; 當程式池中沒有令牌時, 要執行的作業只能等待. 這樣就能保證同時執行的作業數等於令牌數.
管道 = = 》有名管道、無名(匿名)管道
有名管道:mkfilo 建立一個管道檔案
有名管道: “cat 1.file | grep ‘xxx’ ” “|” ==》建立一個無名管道,直接作為兩個程式的資料通道
exec 自行定義,繫結檔案操作符
系統預設三個檔案操作符 0、1、2 = = 》 stdin、stdout、stderr
ls /proc/self/fd
模板
#!/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