1. 程式人生 > >Shell指令碼最佳實踐

Shell指令碼最佳實踐

# Shell指令碼最佳實踐 ## 0. 編碼、縮排、檔案命名和許可權設定等 使用utf-8編碼; 統一使用tab縮排或空格縮排,不要混用; 檔名以`.sh`結尾,並且統一風格; 新增可執行許可權: ```bash chmod +x [bash_script.sh] ``` 最後,在所有輸出完畢後,新增一個空行。 ## 1. 指定預設直譯器 也就是不要省略指令碼第一行的shebang,一般預設是bash: ```bash #!/bin/bash ``` 或者更為通用一些: ```bash #!/usr/bin/env bash ``` 本機可用的shell直譯器,可以通過以下命令檢視: ```bash cat /etc/shells ``` ## 2. Shell環境設定 設定命令回顯: ```bash set -x ``` shell預設設定不夠友好,我們希望予以加強。 ```bash # 遇到未宣告的變數則報錯停止 set -u # 遇到執行錯誤則停止 set -e ``` 由於`set -e`對管道命令無效,管道命令其中一步失敗則中止,需要使用: ```bash set -o pipefail ``` 我們將這三條合併,構成 [bash strict mode](http://redsymbol.net/articles/unofficial-bash-strict-mode/),新增在bash指令碼的開始位置: ```bash set -euo pipefail ``` 因為這裡都是shell環境設定,所以也可以在執行指令碼的時候來使用: ```bash bash -euo pipefail [bash_sctipt.sh] ``` ## 3. 條件判斷。 使用 `[[ ]]` 並在每個變數和運算子以及和括號之間加入一個空格,例如: ```bash if [[ $# > 1 ]] || [[ $# == 1 && $1 != 'PC' && $1 != 'server' ]]; then echo 'Invalid commandline arguments, you should use `./run.sh` or `./run.sh PC` or `./run.sh server`' exit 1 fi ``` 其中,`$#`用於獲取命令列引數個數,`$N`用於獲取第N個命令列引數,引數`$0`指的是指令碼檔名。 相比單方括號,雙方括號的優勢在於可以直接使用比較運算子`>``<``==``!=`等,而不是必須使用`-gt``-lt``-eq``-ne`;此外雙方括號可以使用`&&``||`來表達與和或,而不用必須寫`-a``-o`這種難以記憶的寫法。 ## 4. 使用檔案之前判斷是否存在,並進行異常處理。 ``` # 判斷普通檔案存在 if [[ ! -f 'a.txt' ]]; then touch 'a.txt' fi # 判斷資料夾存在 if [[ ! -d 'src' ]]; then echo 'src dir not found' exit 1 fi ``` 注意`cp -r`命令,在資料夾不存在時回建立資料夾並複製,而當資料夾存在時,會複製到子資料夾內。 ## 5. 迴圈語句。 提倡使用for-in迴圈 ```bash # C風格 for (( i=0; i<10; i++)); do // echo $i done # for-in for i in $(seq 0 9); do // echo $i done ``` 和 if 語句的 then 一樣,for 語句的 do 也緊跟在語句後面,不單獨佔一行,這樣顯得比較緊湊。同樣不要忘記加分號。 ## 6. 總是使用main函式包裹執行體 ```bash main() { func1() func2() } main "$@" ``` 與python類似,shell不需要函式入口,可以從第一條指令開始執行。但是為了可讀性和方便除錯,我們總是寫一個命名為main的函式來作為全域性入口。 ## 7. 變數 1)環境變數的設定和取消: ```bash # 設定環境變數 export SKIP_BFS=1 # 取消環境變數 unset SKIP_BFS ``` 2)區域性變數 shell變數預設全域性作用域,這一點與JavaScript類似,函式內宣告區域性變數,應該新增`local`關鍵字。 3)使用變數時,總是用雙引號把變數包起來,例如: ```bash # 帶空格的路徑 cp -r "$src_dir" "$dest_dir" ``` 路徑有空格會導致很嚴重的bug,用`"$var"`這種寫法,避免了這個問題。 ## 8. 使用`$()`而不是反引號獲取表示式的值 如for-in: ```bash # 建議使用 $(seq lb ub) 而不是 `seq lb ub` 獲取範圍 for i in $(seq 0 10) do echo $i done ``` ## 9. 使用 `/dev/null` 過濾輸出資訊 ```bash [expr] > /dev/null 2>&1 ``` 命令解釋:重定向到空裝置,並把標準錯誤輸出stderr也重定向為stdout。 注意,`2>&1`應該總是放在命令的末尾。 ## 10. case語句等 TBD 更多細節,參考[Google Bash風格指南](https://google.github.io/styleguide/shellguid