shell腳本系列:程式設計風格
阿新 • • 發佈:2020-08-26
函式名
小寫字母、下劃線:
# Single function
my_func() {
...
}
# Part of a package
mypackage::my_func() {
...
}
變數名
小寫字母、下劃線、迴圈變數:
for zone in ${zones}; do
something_with "${zone}"
done
只讀變數
使用readonly或者declare -r宣告:
zip_version="$(dpkg --status zip | grep Version: | cut -d ' ' -f 2)" if [[ -z "${zip_version}" ]]; then error_message else readonly zip_version fi
常量和環境變數
大寫字母:
# Constant
readonly PATH_TO_FILES='/some/path'
# Both constant and environment
declare -xr ORACLE_SID='PROD'
VERBOSE='false'
while getopts 'v' flag; do
case "${flag}" in
v) VERBOSE='true' ;;
esac
done
readonly VERBOSE
使用本地變數
local宣告:
my_func2() { local name="$1" # Separate lines for declaration and assignment: local my_var my_var="$(my_func)" || return # DO NOT do this: $? contains the exit code of 'local', not my_func local my_var="$(my_func)" [[ $? -eq 0 ]] || return ... }
原始檔名
小寫字母、下劃線:
make_template.sh
列印錯誤資訊
err() {
echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $@" >&2
}
if ! do_something; then
err "Unable to do_something"
exit "${E_DID_NOTHING}"
fi
基本語句格式
縮排四個空格,或者一個製表符,一個製表符設定為四個空格。
行的長度最大為80字元,使用\換行。
管道:如果一行容得下整個管道操作,那麼請將整個管道操作寫在同一行。否則,應該將整個管道操作分割成每行一個管段,管道操作的下一部分應該將管道符放在新行並且縮排2個空格。這適用於使用管道符’|’的合併命令鏈以及使用’||’和’&&’的邏輯運算鏈。
# All fits on one line
command1 | command2
# Long commands
command1 \
| command2 \
| command3 \
| command4
for迴圈
請將 ; do , ; then 和 while , for , if 放在同一行。
for dir in ${dirs_to_cleanup}; do
if [[ -d "${dir}/${ORACLE_SID}" ]]; then
log_date "Cleaning up old files in ${dir}/${ORACLE_SID}"
rm "${dir}/${ORACLE_SID}/"*
if [[ "$?" -ne 0 ]]; then
error_message
fi
else
mkdir -p "${dir}/${ORACLE_SID}"
if [[ "$?" -ne 0 ]]; then
error_message
fi
fi
done
case語句
case "${expression}" in
a)
variable="..."
some_command "${variable}" "${other_expr}" ...
;;
absolute)
actions="relative"
another_command "${actions}" "${other_expr}" ...
;;
*)
error "Unexpected expression '${expression}'"
;;
esac
verbose='false'
aflag=''
bflag=''
files=''
while getopts 'abf:v' flag; do
case "${flag}" in
a) aflag='true' ;;
b) bflag='true' ;;
f) files="${OPTARG}" ;;
v) verbose='true' ;;
*) error "Unexpected option ${flag}" ;;
esac
done
變數擴充套件
用 \({var} 而不是 \)var ,詳細解釋如下:
- 與現存程式碼中你所發現的保持一致。
- 引用變數參閱下面一節,引用。
- 除非絕對必要或者為了避免深深的困惑,否則不要用大括號將單個字元的shell特殊變數或定位變數括起來。推薦將其他所有變數用大括號括起來。
引用
set -- 1 "2 two" "3 three tres"; echo $# ; set -- "$*"; echo "$#, $@"
set -- 1 "2 two" "3 three tres"; echo $# ; set -- "$@"; echo "$#, $@"
命令替換
使用 $(command) 而不是反引號`command`。
test、[和[[
推薦使用 [[ ... ]] ,而不是 [ , test , 和 /usr/bin/ [ 。
if [[ "filename" =~ ^[[:alnum:]]+name ]]; then
echo "Match"
fi
if [[ "filename" == "f*" ]]; then
echo "Match"
fi
測試字串
if [[ "${my_var}" = "some_string" ]]; then
do_something
fi
if [[ -n "${my_var}" ]]; then
do_something
fi
# string不空時為真
if [[ -z "${my_var}" ]]; then
do_something
fi
# string空時為真
if [[ "${my_var}" = "" ]]; then
do_something
fi
# string空時為真
檔名的萬用字元擴充套件
因為檔名可能以 - 開頭,所以使用擴充套件萬用字元 ./* 比 * 來得安全得多。
rm -v ./* # 好
rm -v * # 不好
管道導向while迴圈
管道導向while迴圈中的隱式子shell使得追蹤bug變得很困難。
last_line='NULL'
your_command | while read line; do
last_line="${line}"
done
echo "${last_line}"
如果你確定輸入中不包含空格或者特殊符號(通常意味著不是使用者輸入的),那麼可以使用一個for迴圈。
total=0
for value in $(command); do
total+="${value}"
done
使用過程替換允許重定向輸出,但是請將命令放入一個顯式的子shell中,而不是bash為while迴圈建立的隱式子shell。
total=0
last_file=
while read count filename; do
total+="${count}"
last_file="${filename}"
done < <(your_command | uniq -c)
echo "Total = ${total}"
echo "Last one = ${last_file}"
當不需要傳遞複雜的結果給父shell時可以使用while迴圈。這通常需要一些更復雜的“解析”。請注意簡單的例子使用如awk這類工具可能更容易完成。當你特別不希望改變父shell的範圍變數時這可能也是有用的。
cat /proc/mounts | while read src dest type opts rest; do
if [[ ${type} == "nfs" ]]; then
echo "NFS ${dest} maps to ${src}"
fi
done
檢查返回值
mv "${file_list}" "${dest_dir}/"
if [[ "$?" -ne 0 ]]; then
echo "Unable to move ${file_list} to ${dest_dir}" >&2
exit "${E_BAD_MOVE}"
fi