Bash 脚本基础
High Contrast
Dark Mode
Light Mode
Sepia
Forest
2 min read335 words

Bash 脚本基础

只要一个动作要重复三次,就值得考虑写脚本。

生产级脚本标准结构

#!/usr/bin/env bash
# 脚本说明:部署 myapp 到生产服务器
# 用法:./deploy.sh [tag]
# 输入:Git tag(默认 main)
# 输出:部署日志写入 /var/log/myapp/deploy.log
# 失败:退出码非 0,标准错误输出到 stderr
set -euo pipefail
trap 'echo "[ERROR] 脚本在第 $LINENO 行失败" >&2' ERR
# === 常量 ===
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly LOG_DIR=/var/log/myapp
readonly TAG="${1:-main}"
# === 日志函数 ===
log()  { echo "[$(date '+%Y-%m-%d %H:%M:%S')] [INFO]  $*" | tee -a "$LOG_DIR/deploy.log"; }
warn() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] [WARN]  $*" | tee -a "$LOG_DIR/deploy.log" >&2; }
err()  { echo "[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] $*" | tee -a "$LOG_DIR/deploy.log" >&2; }
# === 参数检查 ===
if [[ -z "$TAG" ]]; then
err "TAG 不能为空"
exit 1
fi
log "开始部署 tag=$TAG"
# ... 部署逻辑
log "部署完成"

关键安全选项

选项 作用 没有它会发生什么
set -e 命令非零退出时立即中止脚本 错误后继续执行,可能破坏系统状态
set -u 使用未定义变量时报错 $VAIRABLE 拼写错误被忽略,产生难以追踪的 bug
set -o pipefail 管道任一环节失败即失败 false \| true 整体返回 0,掩盖错误
trap ... ERR 在任意命令失败时执行清理 临时文件、锁文件等资源泄漏

变量与引用规范

# ✅ 正确:始终引用变量(防止空格/特殊字符问题)
file="my file.txt"
cp "$file" /backup/
# ❌ 错误:未引用,空格导致分词
cp $file /backup/    # 等价于 cp my file.txt /backup/,报错
# ✅ 正确:带默认值的变量
APP_ENV="${APP_ENV:-production}"
# ✅ 正确:只读常量
readonly DB_HOST=db.internal
# ✅ 正确:命令替换
NOW=$(date +%Y%m%d)
DIR="$(dirname "$(realpath "$0")")"

函数与错误处理模式

#!/usr/bin/env bash
set -euo pipefail
# 判断命令是否存在
require_cmd() {
command -v "$1" &>/dev/null || { echo "缺少命令: $1"; exit 1; }
}
# 带重试的 curl(网络抖动场景)
fetch_with_retry() {
local url=$1 max_attempts=3 attempt=1
while (( attempt <= max_attempts )); do
curl -sf "$url" && return 0
warn "第 $attempt 次请求失败,${attempt}s 后重试..."
sleep "$attempt"
(( attempt++ ))
done
err "请求失败,已重试 $max_attempts 次: $url"
return 1
}
require_cmd curl
require_cmd jq
fetch_with_retry "https://api.example.com/health"

常见陷阱速查

陷阱 错误写法 正确写法
未引用的变量 rm $DIR/* rm "$DIR"/*
比较用 = vs == if [ $a = $b ] if [[ "$a" == "$b" ]]
检查文件存在 if [ -e $file ] if [[ -f "$file" ]]
算术比较 if [ $n > 5 ] if (( n > 5 ))
子 shell 变量泄漏 $(cmd) 内修改的变量在外部无效 用临时文件或管道传递结果
cd 失败后继续执行 cd /tmp; rm -rf * cd /tmp || exit 1; rm -rf *

本节执行清单

下一节定时任务与巡检脚本——让脚本自动运行起来。