最佳實踐
部分內容由 LLM 生成,尚未經過人工驗證。
Shell 腳本開發最佳實踐與程式碼風格指南。
Shebang 選擇
腳本第一行應指定解釋器:
#!/bin/bash # Bash 專用特性
#!/bin/sh # POSIX 相容(可移植性高)
#!/usr/bin/env bash # 使用 PATH 中的 bash(跨平台)選擇建議:
- 使用 Bash 特性(陣列、
[[ ]])→#!/bin/bash - 需要最大可移植性 →
#!/bin/sh - 跨平台開發(macOS, Linux)→
#!/usr/bin/env bash
嚴格模式 (Strict Mode)
在腳本開頭啟用嚴格模式,提早發現錯誤:
#!/bin/bash
set -euo pipefail
IFS=$'\n\t'選項說明
| 選項 | 說明 |
|---|---|
set -e | 命令失敗時立即退出 |
set -u | 使用未定義變數時報錯 |
set -o pipefail | 管線中任一命令失敗即返回失敗 |
IFS=$'\n\t' | 設定欄位分隔符(避免空格問題) |
# 範例:沒有 set -e
#!/bin/bash
rm non-existent-file # 失敗但繼續執行
echo "Still running" # 會執行
# 範例:有 set -e
#!/bin/bash
set -e
rm non-existent-file # 失敗後立即退出
echo "Won't reach here" # 不會執行選擇性禁用
set -e
# 允許特定命令失敗
command_that_may_fail || true
# 暫時禁用 set -e
set +e
risky_command
set -e程式碼風格
命名慣例
# 常數 - 大寫加底線
readonly MAX_RETRIES=3
readonly CONFIG_FILE="/etc/app.conf"
# 環境變數 - 大寫
export PATH="/usr/local/bin:$PATH"
export DATABASE_URL="postgres://..."
# 局部變數 - 小寫加底線
local user_name="alice"
local file_count=0
# 函式 - 小寫加底線
get_user_info() {
# ...
}
# 私有函式 - 前綴底線(慣例)
_internal_helper() {
# ...
}縮排與格式
# 使用 2 或 4 空格縮排(不使用 tab)
if [[ -f "$file" ]]; then
echo "File exists"
process_file "$file"
fi
# 長命令使用反斜線換行
command --option1 value1 \
--option2 value2 \
--option3 value3
# 管線換行對齊
cat file.txt \
| grep "pattern" \
| sort \
| uniq -c引號使用
# 變數使用雙引號
echo "Hello, $name"
# 避免詞分割(word splitting)
for file in "$@"; do # 正確
echo "$file"
done
for file in $@; do # 錯誤:會被空格分割
echo "$file"
done
# 單引號用於字面字串(不擴展)
echo 'Value is $var' # 輸出: Value is $var註解與文件
函式文件
#######################################
# 計算兩數之和
# Arguments:
# $1 - 第一個數字
# $2 - 第二個數字
# Returns:
# 0 - 成功
# 1 - 參數錯誤
# Outputs:
# 計算結果到 stdout
#######################################
add() {
if [ $# -ne 2 ]; then
echo "Usage: add num1 num2" >&2
return 1
fi
echo $(($1 + $2))
}腳本標頭
#!/bin/bash
#
# backup.sh - 自動備份腳本
#
# Description:
# 備份指定目錄到遠端伺服器
#
# Usage:
# ./backup.sh <source_dir> <dest_server>
#
# Author: Your Name
# Date: 2024-01-15
#
set -euo pipefail使用 ShellCheck 進行靜態分析
ShellCheck 是 Shell 腳本靜態分析工具,可發現常見錯誤與風格問題。
# 安裝
sudo apt install shellcheck # Debian/Ubuntu
brew install shellcheck # macOS
# 檢查腳本
shellcheck script.sh
# 指定 shell
shellcheck --shell=bash script.sh
# 忽略特定警告
shellcheck --exclude=SC2034 script.sh常見警告範例
# SC2086: 變數應加引號
echo $var # 警告
echo "$var" # 正確
# SC2046: 命令替換應加引號
for f in $(ls); do # 警告
for f in *; do # 正確(使用 glob)
# SC2164: cd 應檢查失敗
cd /some/path # 警告
cd /some/path || exit # 正確詳細資訊請參考 ShellCheck 工具說明。
效能考量
避免過度 fork
# 不佳:每次迭代都 fork 新程序
for file in *.txt; do
cat "$file" | grep "pattern"
done
# 較佳:使用 Shell 內建功能
for file in *.txt; do
grep "pattern" "$file"
done
# 最佳:單次呼叫處理所有檔案
grep "pattern" *.txt使用內建命令
# 外部命令(較慢)
dirname=$(dirname "$path")
basename=$(basename "$path")
# Shell 內建(較快)
dirname=${path%/*}
basename=${path##*/}批次操作
# 不佳:逐一處理
for file in *.jpg; do
convert "$file" "${file%.jpg}.png"
done
# 較佳:使用 xargs 並行
find . -name "*.jpg" -print0 | xargs -0 -P 4 -I {} convert {} {}.png可移植性
POSIX 相容寫法
# Bash 專用
[[ $var == "value" ]]
array=("a" "b" "c")
# POSIX 相容
[ "$var" = "value" ]
# 使用空格分隔的字串代替陣列檢測系統差異
# 檢測作業系統
case "$(uname -s)" in
Linux*) machine=Linux;;
Darwin*) machine=Mac;;
CYGWIN*) machine=Cygwin;;
*) machine="UNKNOWN"
esac
echo "Running on $machine"安全考量
避免注入攻擊
# 危險:直接使用使用者輸入
rm -rf $user_input # 若 user_input=/* 會刪除整個系統
# 安全:驗證輸入
if [[ $user_input =~ ^[a-zA-Z0-9_-]+$ ]]; then
rm -rf "$user_input"
else
echo "Invalid input" >&2
exit 1
fi臨時檔案安全
# 不安全
tmpfile="/tmp/myapp.$$"
# 安全:使用 mktemp
tmpfile=$(mktemp) || exit 1
trap "rm -f '$tmpfile'" EXIT
# 臨時目錄
tmpdir=$(mktemp -d) || exit 1
trap "rm -rf '$tmpdir'" EXIT版本管理
# 腳本版本號
readonly VERSION="1.2.3"
# 顯示版本
show_version() {
echo "$(basename "$0") version $VERSION"
}
# 命令列參數處理
while getopts "v" opt; do
case $opt in
v) show_version; exit 0 ;;
*) echo "Usage: $0 [-v]" >&2; exit 1 ;;
esac
done