跳到主要内容

最佳实践

设计原则

1. KISS原则 (Keep It Simple, Stupid)

❌ 错误: 创建复杂的配置系统,需要学习新的DSL
✅ 正确: 用简单的bash脚本和JSON配置

示例:
# 简单有效
function dev() {
cd ~/Developer/$1 && code . && npm run dev
}

# 过度设计
function dev() {
validate_project $1
load_config $1
setup_environment $1
initialize_services $1
configure_editor $1
...
}

2. DRY原则 (Don't Repeat Yourself)

# ❌ 重复代码
function deploy-staging() {
npm test
npm run build
scp -r dist/ staging:/var/www/
}

function deploy-production() {
npm test
npm run build
scp -r dist/ production:/var/www/
}

# ✅ 复用代码
function deploy() {
local env=$1

npm test || return 1
npm run build

case $env in
staging)
scp -r dist/ staging:/var/www/
;;
production)
scp -r dist/ production:/var/www/
;;
esac
}

3. 渐进增强

阶段1: 手动操作 → 记录步骤
阶段2: 写成脚本 → 简化执行
阶段3: 添加参数 → 增加灵活性
阶段4: 错误处理 → 提高可靠性
阶段5: 智能优化 → AI辅助

不要一开始就追求完美!

4. 故障优雅降级

# ✅ 有降级方案
function smart_commit() {
# 尝试AI生成
if command -v claude-cli &> /dev/null; then
msg=$(git diff --cached | claude-cli "Generate commit message")
if [ $? -eq 0 ]; then
git commit -m "$msg"
return
fi
fi

# 降级: 手动输入
read -p "Commit message: " msg
git commit -m "$msg"
}

工作流设计模式

模式1: 流水线模式

适用于: 顺序执行的任务链

# 部署流水线
function deploy() {
echo "1️⃣ Running tests..."
npm test || { echo "❌ Tests failed"; return 1; }

echo "2️⃣ Building..."
npm run build || { echo "❌ Build failed"; return 1; }

echo "3️⃣ Deploying..."
npm run deploy || { echo "❌ Deploy failed"; return 1; }

echo "✅ Deploy successful"
}

模式2: 并行执行模式

适用于: 独立的多任务

# 启动多个服务
function start_all() {
# 并行启动
npm run backend &
npm run frontend &
docker-compose up -d &

# 等待所有完成
wait

echo "✅ All services started"
}

模式3: 条件分支模式

适用于: 不同场景不同处理

function smart_test() {
# 检测改动范围
changed_files=$(git diff --name-only)

if echo "$changed_files" | grep -q "\.test\."; then
# 只运行改动的测试
npm test -- $changed_files
elif echo "$changed_files" | grep -q "src/"; then
# 运行相关测试
npm test -- --related
else
# 快速检查
npm run test:quick
fi
}

模式4: 重试模式

适用于: 可能失败但重试有用的操作

function retry() {
local max_attempts=3
local attempt=1
local command="$@"

while [ $attempt -le $max_attempts ]; do
echo "Attempt $attempt/$max_attempts: $command"

if $command; then
echo "✅ Success"
return 0
fi

echo "❌ Failed, retrying..."
attempt=$((attempt + 1))
sleep 2
done

echo "❌ Failed after $max_attempts attempts"
return 1
}

# 使用
retry npm install
retry curl https://api.example.com

模式5: 缓存模式

适用于: 昂贵的操作

function get_project_info() {
local cache_file="/tmp/project-info-$(pwd | md5).cache"
local cache_ttl=3600 # 1小时

# 检查缓存
if [ -f "$cache_file" ]; then
local age=$(($(date +%s) - $(stat -f %m "$cache_file")))
if [ $age -lt $cache_ttl ]; then
cat "$cache_file"
return
fi
fi

# 重新计算
local info=$(expensive_calculation)
echo "$info" | tee "$cache_file"
}

命名规范

脚本命名

✅ 好的命名:
- dev-start.sh (清晰的动词-名词)
- git-smart-commit.sh (带前缀分类)
- note-quick-add.sh (描述性)

❌ 不好的命名:
- script.sh (太泛泛)
- utils.sh (不具体)
- my_thing.sh (不清楚)

函数命名

# ✅ 动词开头,清晰表达意图
function start_dev_server() { }
function check_git_status() { }
function generate_report() { }

# ❌ 模糊不清
function do_stuff() { }
function helper() { }
function run() { }

变量命名

# ✅ 描述性,用大写表示常量
PROJECT_DIR="$HOME/Developer"
MAX_RETRIES=3
current_branch=$(git branch --show-current)

# ❌ 无意义的名字
d="$HOME/Developer"
x=3
tmp=$(git branch --show-current)

错误处理

1. 始终检查关键操作

# ❌ 不检查错误
cd "$PROJECT_DIR"
rm -rf node_modules

# ✅ 检查并处理
if [ ! -d "$PROJECT_DIR" ]; then
echo "❌ Project directory not found"
exit 1
fi

cd "$PROJECT_DIR" || exit 1
rm -rf node_modules

2. 提供有用的错误信息

# ❌ 错误信息不明确
echo "Error"

# ✅ 清晰的上下文
echo "❌ Failed to connect to database"
echo " Check if PostgreSQL is running: brew services list"
echo " Connection string: $DB_URL"

3. 使用trap清理资源

function cleanup() {
echo "🧹 Cleaning up..."
# 停止后台进程
kill $SERVER_PID 2>/dev/null
# 删除临时文件
rm -f /tmp/my-script-*
}

trap cleanup EXIT

# 你的脚本逻辑
npm run dev &
SERVER_PID=$!

性能优化

1. 避免不必要的操作

# ❌ 每次都检查
if [ -f "package.json" ]; then
npm install
fi

# ✅ 只在需要时安装
if [ -f "package.json" ] && [ ! -d "node_modules" ]; then
npm install
elif [ "package.json" -nt "node_modules" ]; then
npm install
fi

2. 并行化独立任务

# ❌ 串行执行 (慢)
lint_code
run_tests
build_project

# ✅ 并行执行 (快)
lint_code &
run_tests &
build_project &
wait

3. 使用合适的工具

# ❌ 慢
find . -name "*.js" -exec cat {} \; | wc -l

# ✅ 快
rg --files --type js | xargs wc -l

安全实践

1. 永不硬编码密钥

# ❌ 危险
API_KEY="sk-1234567890abcdef"

# ✅ 从环境变量读取
API_KEY="${API_KEY:-}"
if [ -z "$API_KEY" ]; then
echo "❌ API_KEY not set"
exit 1
fi

# 或从加密存储读取
API_KEY=$(security find-generic-password -s "my-api-key" -w)

2. 限制文件权限

# 创建敏感配置文件
touch ~/.config/secrets.env
chmod 600 ~/.config/secrets.env # 只有所有者可读写

3. 验证输入

function deploy() {
local env=$1

# 验证环境
if [[ ! "$env" =~ ^(dev|staging|production)$ ]]; then
echo "❌ Invalid environment: $env"
echo " Valid: dev, staging, production"
exit 1
fi

# 生产环境额外确认
if [ "$env" = "production" ]; then
read -p "⚠️ Deploy to PRODUCTION? (yes/no): " confirm
if [ "$confirm" != "yes" ]; then
echo "Cancelled"
exit 0
fi
fi

# 继续部署...
}

4. 审计日志

function log_action() {
local action=$1
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$timestamp] $USER: $action" >> ~/.automation/audit.log
}

function sensitive_operation() {
log_action "Deploying to production"
# 执行操作...
}

可维护性

1. 文档化

#!/bin/bash
#
# 脚本名称: smart-deploy.sh
# 描述: 智能部署脚本,支持多环境
# 作者: Your Name
# 创建日期: 2025-01-01
# 最后修改: 2025-01-15
#
# 使用方法:
# ./smart-deploy.sh <environment> [options]
#
# 参数:
# environment 部署环境 (dev|staging|production)
# --skip-tests 跳过测试 (不推荐)
# --force 强制部署
#
# 示例:
# ./smart-deploy.sh staging
# ./smart-deploy.sh production --force
#

function deploy() {
# 函数文档
# 执行部署流程
# 参数: $1 - 环境名称
# 返回: 0 成功, 1 失败
...
}

2. 模块化

# 不要把所有代码放一个文件

# lib/utils.sh
function log() { echo "[$(date)] $*"; }
function error() { echo "❌ $*" >&2; }

# lib/git.sh
function check_git_clean() { ... }
function get_current_branch() { ... }

# main.sh
source lib/utils.sh
source lib/git.sh

function main() {
log "Starting..."
...
}

main "$@"

3. 版本控制

# 所有自动化脚本都应该在Git中管理
mkdir -p ~/.automation
cd ~/.automation
git init

# .gitignore
echo "*.log" >> .gitignore
echo "*.cache" >> .gitignore
echo "secrets.env" >> .gitignore

# 提交
git add .
git commit -m "Initial automation setup"

测试策略

1. 干运行模式

DRY_RUN=false

function execute() {
local cmd=$1

if [ "$DRY_RUN" = true ]; then
echo "[DRY RUN] $cmd"
else
eval "$cmd"
fi
}

# 使用
execute "rm -rf node_modules"
execute "npm install"

# 测试时: DRY_RUN=true ./script.sh

2. 单元测试函数

# test-utils.sh
function test_extract_project_name() {
local input="/home/user/projects/my-app"
local expected="my-app"
local result=$(extract_project_name "$input")

if [ "$result" = "$expected" ]; then
echo "✅ test_extract_project_name passed"
else
echo "❌ test_extract_project_name failed"
echo " Expected: $expected"
echo " Got: $result"
exit 1
fi
}

# 运行测试
test_extract_project_name

3. 集成测试

# integration-test.sh
function test_full_workflow() {
echo "Testing full deployment workflow..."

# 创建测试项目
mkdir -p /tmp/test-project
cd /tmp/test-project
git init
echo "test" > README.md
git add .
git commit -m "test"

# 执行工作流
if ../scripts/deploy.sh test; then
echo "✅ Integration test passed"
else
echo "❌ Integration test failed"
exit 1
fi

# 清理
cd -
rm -rf /tmp/test-project
}

test_full_workflow

常见陷阱和解决方案

陷阱1: 路径问题

# ❌ 问题: 相对路径在不同目录执行会出错
cd scripts
./deploy.sh

# ✅ 解决: 使用绝对路径
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"

陷阱2: 空格问题

# ❌ 问题: 文件名有空格
for file in $(ls); do
echo $file # 空格会被拆分
done

# ✅ 解决: 正确引用
while IFS= read -r file; do
echo "$file"
done < <(ls)

# 或者
for file in *; do
echo "$file"
done

陷阱3: 子shell问题

# ❌ 问题: 变量在子shell中修改不生效
cat file.txt | while read line; do
count=$((count + 1))
done
echo $count # 还是0

# ✅ 解决: 避免管道
while read line; do
count=$((count + 1))
done < file.txt
echo $count # 正确

陷阱4: 过度自动化

症状:
- 配置复杂度超过任务本身
- 花更多时间维护自动化而不是使用
- 团队其他人看不懂

解决:
- 保持简单
- 文档化
- 渐进增强
- 定期审查:这个自动化还有用吗?

检查清单

脚本发布前

  • 添加shebang (#!/bin/bash)
  • 添加注释和文档
  • 检查错误处理
  • 测试边界情况
  • 设置正确的文件权限
  • 无硬编码的密钥
  • 有使用示例

工作流设计前

  • 真的需要自动化吗?
  • 频率够高吗?
  • 有降级方案吗?
  • 错误时怎么办?
  • 如何监控?
  • 如何测试?
  • 团队能用吗?

定期审查 (每月)

  • 哪些自动化最有用?
  • 哪些从不使用?
  • 有新的痛点吗?
  • 可以合并简化吗?
  • 文档是最新的吗?
  • 有安全问题吗?

记住: 最好的代码是不写的代码。在自动化之前,先问"这是必要的吗?"