Bash 最佳實踐和技巧
progrium/bashstyle (中文翻譯 yujiaao)
Bash 是系統程式設計的 JavaScript. 但有時使用系統程式語言如 C 或 Go 會更好, Bash 是理想的面向 POSIX 標準的小任務或命令列級系統程式語言, 下面是三個顯見的原因:
- 它無處不在. 尤如 JavaScript 對 web 而言, Bash 在這裡併為系統程式設計已準備好.
- 它是中性的. 不同於 Ruby, Python, JavaScript, 或 PHP, Bash 平等地得罪了全部社群. ;)
- 它生為膠水. 複雜部分用 C 或 Go (或其他什麼語言!)來寫, 然後用 Bash 把他們粘在一塊.
本文件描述了我怎麼寫 Bash 以及我希望如何與合作者一起在我的開源專案裡寫 Bash. 本文基於大量經驗和花了大量時間收集最佳實踐. 多數來自這
兩篇文章 ,
但此處整合, 略有修改, 並聚焦在最值錢的點上. 加上一些新東西!
記住本文不是講一般的 shell 指令碼程式設計, 這裡是專門針對 Bash 和 Bash 作為直譯器的規則.
大規則
-
總是用雙引號括起變數, 包括子shell. 不要裸體的
$
符號.- 這規則會帶你走老遠了. 詳見http://mywiki.wooledge.org/Qu...
-
全部程式碼進函式. 即使只有一個函式,
main
.main readonly
-
可執行指令碼總有
main
函式, 呼叫時用main
或main "$@"
-
如果指令碼也用作庫, 呼叫時用
[[ "$0" == "$BASH_SOURCE" ]] && main "$@"
判斷
-
如果指令碼也用作庫, 呼叫時用
-
設定變數時總是用
local
修飾, 除非有理由用declare
- 少數例外情況是當你故意想在外層空間設定一個變數.
- 變數都用小寫, 除非想輸出到環境中.
-
總用
set -eo pipefail
. 快速失敗並檢查退出狀態(exit codes).-
在那些你有意讓退出狀態不為零的程式裡使用
|| true
.
-
在那些你有意讓退出狀態不為零的程式裡使用
-
切勿使用棄用的樣式. 最值得注意的:
-
函式定義成
myfunc() { ... }
, 而不是function myfunc { ... }
-
判斷條件總是用
[[
替代[
或test
-
從不使用反引號, 使用
$( ... )
- 更多詳見http://wiki.bash-hackers.org/...
-
函式定義成
-
優先使用絕對路徑 (藉助 $PWD), 總用
./
來修飾相對路徑. -
兩行以上的函式總是在頂部用
declare
聲名變數和為引數命名declare arg1="$1" arg2="$2"
-
使用
mktemp
建立臨時檔案, 總是用trap
來清理. - 警告和錯誤應該傳送到 STDERR, 任何可解析的內容都應該轉到 STDOUT.
-
完成後嘗試本地化
shopt
使用和禁用選項.
如果你知道你在做什麼,你可以曲解或破壞其中的一些規則,但通常它們是正確的並且非常有幫助。
最佳實踐和技巧
- 如果可能,請在 awk/sed 之前使用 Bash 變數替換.
- 通常使用雙引號,除非使用單引號更有意義.
-
對於簡單的條件,請嘗試使用
&&
和||
. -
不要害怕
printf
, 它比echo
更強大. -
把
then
,do
, 等入在同一行, 不換行. -
如果您可以測試退出程式碼, 請避免在 if 表示式中中使用
[[ ... ]]
. -
如果要包含/原始碼執行檔案請使用
.sh
或.bash
副檔名. 但不用在可執行指令碼上. -
把複雜的單行
sed
,perl
命令等放在一個獨立的函式中, 且使用描述性名稱. -
包含
[[ "$TRACE" ]] && set -x
是個好主意. -
做簡單明瞭的設計.
- 避免使用選項標誌和解析,而是嘗試使用可選的環境變數.
- 將子命令用於必要的不同“模式”.
-
在大型系統或任何CLI命令中,向函式新增描述.
-
在函式頂部使用
declare desc="description"
,甚至在引數宣告之上. -
這可以使用反射查詢/提取。例如:
eval $(type FUNCTION_NAME | grep 'declare desc=') && echo "$desc"
-
在函式頂部使用
- 意識到需要可移植性。在容器中執行 Bash 可以比 Bash 在多個平臺上執行做出更多的假設.
- 在期望或匯出環境變數時,考慮可能涉及子 shell 的名稱空間變數.
- 使用硬tab符。Heredocs忽略了前導tab符,允許更好的縮排.
好的參考和幫助
- http://tldp.org/LDP/abs/html/
- 互動式Bash提示:http://samrowe.com/wordpress/...
- 供參考,谷歌的Bash風格指南
- 對於標準檢核,shellcheck
例子
具有命名引數的常規函式
使用引數定義函式
regular_func() { declare arg1="$1" arg2="$2" arg3="$3" # ... }
變參函式
使用最終的可變引數定義函式
variadic_func() { local arg1="$1"; shift local arg2="$1"; shift local rest="$@" # ... }
條件:測試退出程式碼與輸出
# 測試退出程式碼(-q靜音輸出) if grep -q 'foo' somefile; then ... fi # 測試輸出( -m1 限制一個結果) if [[ "$(grep -m1 'foo' somefile)" ]]; then ... fi