Shell指令碼最佳實踐

與MPI做鬥爭發表於2020-08-23

Shell指令碼最佳實踐

0. 編碼、縮排、檔案命名和許可權設定等

使用utf-8編碼;
統一使用tab縮排或空格縮排,不要混用;
檔名以.sh結尾,並且統一風格;
新增可執行許可權:

chmod +x [bash_script.sh]

最後,在所有輸出完畢後,新增一個空行。

1. 指定預設直譯器

也就是不要省略指令碼第一行的shebang,一般預設是bash:

#!/bin/bash

或者更為通用一些:

#!/usr/bin/env bash

本機可用的shell直譯器,可以通過以下命令檢視:

cat /etc/shells

2. Shell環境設定

設定命令回顯:

set -x

shell預設設定不夠友好,我們希望予以加強。

# 遇到未宣告的變數則報錯停止
set -u
# 遇到執行錯誤則停止
set -e

由於set -e對管道命令無效,管道命令其中一步失敗則中止,需要使用:

set -o pipefail

我們將這三條合併,構成 bash strict mode,新增在bash指令碼的開始位置:

set -euo pipefail

因為這裡都是shell環境設定,所以也可以在執行指令碼的時候來使用:

bash -euo pipefail [bash_sctipt.sh]

3. 條件判斷。

使用 [[ ]] 並在每個變數和運算子以及和括號之間加入一個空格,例如:

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迴圈

# 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函式包裹執行體

main() {
      func1()
      func2()
}
main "$@"

與python類似,shell不需要函式入口,可以從第一條指令開始執行。但是為了可讀性和方便除錯,我們總是寫一個命名為main的函式來作為全域性入口。

7. 變數

1)環境變數的設定和取消:

# 設定環境變數
export SKIP_BFS=1
# 取消環境變數
unset SKIP_BFS

2)區域性變數
shell變數預設全域性作用域,這一點與JavaScript類似,函式內宣告區域性變數,應該新增local關鍵字。

3)使用變數時,總是用雙引號把變數包起來,例如:

# 帶空格的路徑
cp -r "$src_dir" "$dest_dir"

路徑有空格會導致很嚴重的bug,用"$var"這種寫法,避免了這個問題。

8. 使用$()而不是反引號獲取表示式的值

如for-in:

# 建議使用 $(seq lb ub) 而不是 `seq lb ub` 獲取範圍
for i in $(seq 0 10) do 
      echo $i
done

9. 使用 /dev/null 過濾輸出資訊

[expr] > /dev/null 2>&1

命令解釋:重定向到空裝置,並把標準錯誤輸出stderr也重定向為stdout。
注意,2>&1應該總是放在命令的末尾。

10. case語句等

TBD

更多細節,參考Google Bash風格指南

相關文章