您好,登錄后才能下訂單哦!
這篇文章主要講解了“編寫(xiě)可靠Bash腳本的技巧有哪些”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“編寫(xiě)可靠Bash腳本的技巧有哪些”吧!
在寫(xiě)腳本時(shí),在一開(kāi)始(Shebang 之后)加上下面這一句,或者它的縮略版,能避免很多問(wèn)題,更重要的是能讓很多隱藏的問(wèn)題暴露出來(lái):
set -xeuo pipefail
下面說(shuō)明每個(gè)參數(shù)的作用,以及一些例外的處理方式 :
-x :在執(zhí)行每一個(gè)命令之前把經(jīng)過(guò)變量展開(kāi)之后的命令打印出來(lái)。
這個(gè)對(duì)于 debug 腳本、輸出 Log 時(shí)非常有用。正式運(yùn)行的腳本也可以不加。
-e :遇到一個(gè)命令失敗(返回碼非零)時(shí),立即退出。
bash 跟其它的腳本語(yǔ)言最大的不同點(diǎn)之一,應(yīng)該就是遇到異常時(shí)繼續(xù)運(yùn)行下一條命令。這在很多時(shí)候會(huì)遇到意想不到的問(wèn)題。加上 -e ,會(huì)讓 bash 在遇到一個(gè)命令失敗時(shí),立即退出。
如果有時(shí)確實(shí)需要忽略個(gè)別命令的返回碼,可以用 || true 。如:
some_cmd || true # 即使some_cmd失敗了,仍然會(huì)繼續(xù)運(yùn)行some_cmd || RET=$? # 或者可以這樣來(lái)收集some_cmd的返回碼,供后面的邏輯判斷使用
但是在管道串起多條命令的情況下,只有最后一條命令失敗時(shí)才會(huì)退出。如果想讓管道中任意一條命令失敗就退出,就要用后面提到的-o pipefail 了。
加-e 有時(shí)候可能會(huì)不太方便,動(dòng)不動(dòng)就退出。但還是應(yīng)該堅(jiān)持所謂的fail-fast 原則,也就是有異常時(shí)停止正常運(yùn)行,而不是繼續(xù)嘗試運(yùn)行可能存在缺陷的過(guò)程。如果有命令可以明確忽略異常,那可以用上面提到的 || true 等方式明確地忽略之。
-u :試圖使用未定義的變量,就立即退出。
如果在 bash 里使用一個(gè)未定義的變量,默認(rèn)是會(huì)展開(kāi)成一個(gè)空串。有時(shí)這種行為會(huì)導(dǎo)致問(wèn)題,比如:
rm -rf $MYDIR/data
如果 MYDIR 變量因?yàn)槟撤N原因沒(méi)有賦值,這條命令就會(huì)變成 rm -rf /data 。這就比較搞笑了。。使用 -u 可以避免這種情況。
但有時(shí)候在已經(jīng)設(shè)置了-u 后,某些地方還是希望能把未定義變量展開(kāi)為空串,可以這樣寫(xiě):
${SOME_VAR:-}# bash變量展開(kāi)語(yǔ)法,可以參考:https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html
-o pipefail :只要管道中的一個(gè)子命令失敗,整個(gè)管道命令就失敗。
pipefail 與-e 結(jié)合使用的話,就可以做到管道中的一個(gè)子命令失敗,就退出腳本。
1. 防止重疊運(yùn)行
在一些場(chǎng)景中,我們通常不希望一個(gè)腳本有多個(gè)實(shí)例在同時(shí)運(yùn)行。比如用 crontab 周期性運(yùn)行腳本時(shí),有時(shí)不希望上一個(gè)輪次還沒(méi)運(yùn)行完,下一個(gè)輪次就開(kāi)始運(yùn)行了。這時(shí)可以用 flock 命令來(lái)解決。flock 通過(guò)文件鎖的方式來(lái)保證獨(dú)占運(yùn)行,并且還有一個(gè)好處是進(jìn)程退出時(shí),文件鎖也會(huì)自動(dòng)釋放,不需要額外處理。
用法 1:假設(shè)你的入口腳本是 myscript.sh,可以新建一個(gè)腳本,通過(guò) flock 來(lái)運(yùn)行它:
# flock --wait 超時(shí)時(shí)間 -e 鎖文件 -c "要執(zhí)行的命令" # 例如: flock --wait 5 -e "lock_myscript" -c "bash myscript.sh"
用法 2:也可以在原有腳本里使用 flock??梢园盐募蜷_(kāi)為一個(gè)文件描述符,然后使用 flock 對(duì)它上鎖(flock 可以接受文件描述符參數(shù))。
exec 123<>lock_myscript # 把lock_myscript打開(kāi)為文件描述符123 flock --wait 5 123 || { echo 'cannot get lock, exit'; exit 1; }
2. 意外退出時(shí)殺掉所有子進(jìn)程
我們的腳本通常會(huì)啟動(dòng)好多子腳本和子進(jìn)程,當(dāng)父腳本意外退出時(shí),子進(jìn)程其實(shí)并不會(huì)退出,而是繼續(xù)運(yùn)行著。如果腳本是周期性運(yùn)行的,有可能發(fā)生一些意想不到的問(wèn)題。
在 stackoverflow 上找到的一個(gè)方法,原理就是利用 trap 命令在腳本退出時(shí) kill 掉它整個(gè)進(jìn)程組。把下面的代碼加在腳本開(kāi)頭區(qū),實(shí)測(cè)管用:
trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT
不過(guò)如果父進(jìn)程是用 SIGKILL (kill -9) 殺掉的,就不行了。因?yàn)?SIGKILL 時(shí),進(jìn)程是沒(méi)有機(jī)會(huì)運(yùn)行任何代碼的。
3. timeout 限制運(yùn)行時(shí)間
有時(shí)候需要對(duì)命令設(shè)置一個(gè)超時(shí)時(shí)間。這時(shí)可以使用 timeout 命令,用法很簡(jiǎn)單:
timeout 600s some_command arg1 arg2
命令在超時(shí)時(shí)間內(nèi)運(yùn)行結(jié)束時(shí),返回碼為 0,否則會(huì)返回一個(gè)非零返回碼。
timeout 在超時(shí)時(shí)默認(rèn)會(huì)發(fā)送 TERM 信號(hào),也可以用 -s 參數(shù)讓它發(fā)送其它信號(hào)。
4. 連續(xù)管道時(shí),考慮使用 tee 將中間結(jié)果落盤(pán),以便查問(wèn)題
有時(shí)候我們會(huì)用到把好多條命令用管道串在一起的情況。如 cmd1 | cmd2 | cmd3 | ...這樣會(huì)讓問(wèn)題變得難以排查,因?yàn)橹虚g數(shù)據(jù)我們都看不到。
如果改成這樣的格式:
cmd1 > out1.dat cat out1 | cmd2 > out2.dat cat out2 | cmd3 > out3.dat
性能又不太好,因?yàn)檫@樣 cmd1, cmd2, cmd3 是串行運(yùn)行的,這時(shí)可以用 tee 命令:
cmd1 | tee out1.dat | cmd2 | tee out2.dat | cmd3 > out3.dat
感謝各位的閱讀,以上就是“編寫(xiě)可靠Bash腳本的技巧有哪些”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)編寫(xiě)可靠Bash腳本的技巧有哪些這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎ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)容。