您好,登錄后才能下訂單哦!
這篇文章將為大家詳細(xì)講解有關(guān)如何正經(jīng)的實(shí)現(xiàn)shell腳本單例運(yùn)行,小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。
看起來(lái)可行的方法
一個(gè)非常簡(jiǎn)單的思路就是,新的腳本被執(zhí)行時(shí),先檢測(cè)當(dāng)前腳本是否有其他實(shí)例正在運(yùn)行,如果有則直接退出。
runCount=$(ps -ef|grep test.sh | grep -v grep -c) if [ "${runCount}" -ge 1 ] then echo -e "test.sh already running,num:${runCount}" exit 1; fi while true do echo "test.sh run" sleep 1 done
這里通過(guò)ps獲取到當(dāng)前在運(yùn)行的test.sh腳本數(shù),如果大于1,說(shuō)明已經(jīng)有在運(yùn)行的了。
但是你運(yùn)行會(huì)發(fā)現(xiàn),其程序數(shù)量不只是一個(gè)。
$ ./test.sh test.sh already running,num:2
驚不驚喜?為什么為這樣呢?原因在于,shell腳本中一個(gè)命令執(zhí)行相當(dāng)于fork了一個(gè)進(jìn)程執(zhí)行,這里執(zhí)行的是查找tesh.sh并grep的程序,另外還有一個(gè)就是當(dāng)前運(yùn)行的腳本程序,這樣的方式自然就會(huì)出現(xiàn)每次都有兩個(gè)了。
當(dāng)然判斷條件這里你可以換一下,例如數(shù)量大于2,但終歸不太好。
文件鎖
實(shí)際上這種方法你已經(jīng)在《如何讓你的程序同時(shí)只有一個(gè)在運(yùn)行》介紹過(guò)了,只不過(guò)之前是用于編寫(xiě)C/C++程序,而這里是用于shell腳本。
我們來(lái)回顧一下,這是一個(gè)怎樣的過(guò)程:
運(yùn)行前檢查是否有該鎖文件,并且文件中的進(jìn)程正在運(yùn)行
如果有并且程序正在運(yùn)行,則已經(jīng)有實(shí)例在運(yùn)行
否則,無(wú)實(shí)例,創(chuàng)建鎖文件,寫(xiě)入進(jìn)程id
退出時(shí),刪除鎖文件
解釋一下第一條,為什么一定要確定鎖文件中的進(jìn)程正在運(yùn)行,因?yàn)椋行┣闆r下如果運(yùn)行的時(shí)候退出沒(méi)有刪除該文件,則會(huì)導(dǎo)致新的實(shí)例永遠(yuǎn)無(wú)法運(yùn)行。
#!/usr/bin/env bash LOCKFILE=/tmp/test.lock if [ -e ${LOCKFILE} ] && kill -0 `cat ${LOCKFILE}`; then echo " $0 already running" exit fi # 確保退出時(shí),鎖文件被刪除 trap "rm -f ${LOCKFILE}; exit" INT TERM EXIT #將當(dāng)前程序進(jìn)程id寫(xiě)入鎖文件 echo $$ > ${LOCKFILE} # 做你需要的事情 sleep 1000 # 刪除鎖文件 rm -f ${LOCKFILE}
我們?cè)囍\(yùn)行其中一個(gè),然后另外一個(gè)窗口嘗試運(yùn)行:
$ ./test.sh ./test.sh already running
由于已經(jīng)有實(shí)例在運(yùn)行,發(fā)現(xiàn)新的程序無(wú)法運(yùn)行了。而等舊的腳本運(yùn)行完之后,新的就可以運(yùn)行了。
實(shí)際上這里面有幾個(gè)點(diǎn)非常巧妙:
kill -0 `cat \${LOCKFILE}` 這里用于檢測(cè)該進(jìn)程是否存在,避免進(jìn)程不在了,但是鎖文件還在,導(dǎo)致后面的腳本無(wú)法運(yùn)行。
trap "rm -f \${LOCKFILE}; exit" INT TERM EXIT 用于確保腳本退出時(shí),鎖文件會(huì)被刪除。
rm -f {LOCKFILE} 腳本最后需要?jiǎng)h除鎖文件
flock
說(shuō)到鎖文件,這里就不得不提flock命令了。沒(méi)有前面的一些巧妙處理,我們很多時(shí)候會(huì)很難刪除原先創(chuàng)建的鎖文件,比如:
腳本被意外中斷,沒(méi)來(lái)得及執(zhí)行刪除
多個(gè)腳本產(chǎn)生競(jìng)爭(zhēng),導(dǎo)致判斷異常,比如前面有一個(gè)腳本運(yùn)行,判斷沒(méi)有鎖文件,下一步準(zhǔn)備創(chuàng)建,但是另外一個(gè)腳本又先創(chuàng)建了,就會(huì)導(dǎo)致異常了。
因此我們可以考慮使用flock:
#!/usr/bin/env bash LOCK_FILE=/tmp/test.lock exec 99>"$LOCK_FILE" flock -n 99 if [ "$?" != 0 ]; then echo "$0 already running" exit 1 fi #腳本要做的其他事情 sleep 1024
解釋一下:
exec 99>"$LOCK_FILE" 表示創(chuàng)建文件描述符99,指向鎖文件,為何是99?110其實(shí)也是可以的,只是為了和當(dāng)前腳本可能打開(kāi)的文件描述符沖突(例如和0,1,2沖突)。
flock -n 99 嘗試對(duì)該文件描述符加鎖,由操作系統(tǒng)保證原子性
一旦flock失敗了,我們這里可以退出
而即使鎖定了,腳本退出后,也會(huì)自動(dòng)釋放
因此這里避免了鎖沒(méi)有釋放的情況。
另一種做法
查看flock的man手冊(cè),我們發(fā)現(xiàn)它還有一個(gè)例子是這么做的:
[ "${FLOCKER}" != "$0" ] && exec env FLOCKER="$0" flock -en "$0" "$0" "$@" || :
在腳本開(kāi)頭加上上面這么一行就可以了。例如:
#!/usr/bin/env bash [ "${FLOCKER}" != "$0" ] && exec env FLOCKER="$0" flock -en "$0" "$0" "$@" || : #腳本要做的其他事情 sleep 1024
解釋一下:如果${FLOCKER}環(huán)境變量沒(méi)有設(shè)置,則嘗試將腳本本身加鎖,如果加鎖成功,則運(yùn)行當(dāng)前腳本,(并且?guī)显械膮?shù)),否則的話(huà)靜默退出。
總結(jié)
單例運(yùn)行本身思路是很簡(jiǎn)單的,就是探測(cè)當(dāng)前是否有實(shí)例在運(yùn)行,如果有,則退出,但是這里如何判斷,卻并不是那么容易。
最后,總結(jié)一下本文出現(xiàn)的一些該掌握的信息:
$0 腳本名稱(chēng)
$@ 腳本參數(shù)
$$ 當(dāng)前腳本進(jìn)程id
$? 上一條命令執(zhí)行結(jié)果
描述符0 標(biāo)準(zhǔn)輸入
描述符1 標(biāo)準(zhǔn)輸出
描述符2 標(biāo)準(zhǔn)錯(cuò)誤
> 重定向
關(guān)于“如何正經(jīng)的實(shí)現(xiàn)shell腳本單例運(yùn)行”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,使各位可以學(xué)到更多知識(shí),如果覺(jué)得文章不錯(cuò),請(qǐng)把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。