Shell脚本是自动化运维的核心工具。从简单的备份脚本到复杂的部署流水线,Shell无处不在。一个优秀的DevOps工程师必须精通Shell脚本编程。
.sh文件 → 读取 → 词法分析 → 语法解析 → 执行
│ │
│ ┌────────────────────────────┐ │
└────▶│ #!/bin/bash (Shebang) │ │
│ 变量定义 │ │
│ 函数定义 │ │
│ 主逻辑 │ │
│ 条件判断/循环 │ │
│ 命令执行 │◀┘
│ 返回退出码 │
└──────────────────────────┘
│
0 = 成功
非0 = 失败
set -euo pipefail:任何错误立即退出"$VAR"防止分词和通配[[ ]]代替[ ]:更安全更强大#!/usr/bin/env bash
# deploy.sh - 应用部署脚本
# 作者: DevOps Team
# 用法: ./deploy.sh [环境] [版本]
set -euo pipefail
# ============ 配置 ============
readonly SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
readonly APP_NAME="myapp"
readonly LOG_FILE="$SCRIPT_DIR/deploy.log"
# ============ 颜色输出 ============
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
log() {{ echo -e "${{GREEN}}[INFO]${{NC}} $(date '+%Y-%m-%d %H:%M:%S') $*"; }}
warn() {{ echo -e "${{YELLOW}}[WARN]${{NC}} $(date '+%Y-%m-%d %H:%M:%S') $*"; }}
error() {{ echo -e "${{RED}}[ERROR]${{NC}} $(date '+%Y-%m-%d %H:%M:%S') $*" >&2; exit 1; }}
# ============ 参数解析 ============
ENV="${{1:-staging}}"
VERSION="$${{2:-latest}}"
# ============ 前置检查 ============
check_prerequisites() {{
log "检查前置条件..."
command -v docker >/dev/null 2>&1 || error "需要docker"
command -v kubectl >/dev/null 2>&1 || error "需要kubectl"
[[ -f "$SCRIPT_DIR/config/$ENV.yaml" ]] || error "缺少配置: $ENV.yaml"
log "前置条件检查通过 ✅"
}}
# ============ 部署函数 ============
deploy() {{
log "开始部署 $APP_NAME v$VERSION 到 $ENV"
# 部署逻辑...
log "部署完成 ✅"
}}
# ============ 主流程 ============
main() {{
log "========== 部署启动 =========="
check_prerequisites
deploy
log "========== 部署结束 =========="
}}
main "$@"
# 条件判断
if [[ "$ENV" == "prod" ]]; then
REPLICAS=3
MEMORY="1Gi"
elif [[ "$ENV" == "staging" ]]; then
REPLICAS=1
MEMORY="512Mi"
else
REPLICAS=1
MEMORY="256Mi"
fi
# 文件判断
[[ -f "$CONFIG" ]] && echo "文件存在"
[[ -d "$DIR" ]] && echo "目录存在"
[[ -r "$FILE" ]] && echo "可读"
[[ -x "$SCRIPT" ]] && echo "可执行"
[[ -s "$FILE" ]] && echo "文件非空"
# for循环
for host in web1 web2 web3; do
ssh "$host" "sudo systemctl restart nginx"
done
# while循环(逐行读取)
while IFS= read -r line; do
echo "处理: $line"
done < server-list.txt
# 并行执行
for host in web1 web2 web3; do
ssh "$host" "uptime" &
done
wait # 等待所有后台任务完成
echo "所有服务器检查完成"
# grep - 文本搜索
grep -r "ERROR" /var/log/ # 递归搜索
grep -E "ERROR|WARN|FATAL" app.log # 扩展正则
grep -c "200" access.log # 匹配行数
grep -A5 -B2 "Exception" app.log # 上下文
# sed - 流编辑器
sed -i 's/oldhost/newhost/g' /etc/hosts # 替换
sed -n '10,20p' file.txt # 打印行范围
sed '/^#/d' config.cfg # 删除注释行
sed -i "s/VERSION=.*/VERSION=$NEW_VER/" .env
# awk - 文本分析
awk '{{print $1, $4}}' access.log # 提取列
awk '$9 == "500" {{count++}} END {{print count}}' access.log # 统计500
awk '{{sum+=$10}} END {{print "Total:", sum}}' data.txt
awk -F: '{{print $1}}' /etc/passwd # 指定分隔符
# 实战:分析Nginx访问日志
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10
# 统计Top10 IP访问量
#!/usr/bin/env bash
# auto-backup.sh - 自动备份脚本
set -euo pipefail
readonly BACKUP_DIR="/data/backups"
readonly RETENTION_DAYS=30
readonly TIMESTAMP=$(date +%Y%m%d_%H%M%S)
log() {{ echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }}
# 创建备份目录
mkdir -p "$BACKUP_DIR"/{{mysql,redis,config}}
# 备份MySQL
backup_mysql() {{
log "开始备份MySQL..."
mysqldump -u backup -p"$(cat /run/secrets/mysql_pass)" \
--all-databases --single-transaction \
| gzip > "$BACKUP_DIR/mysql/all_$TIMESTAMP.sql.gz"
log "MySQL备份完成 ✅"
}}
# 备份Redis
backup_redis() {{
log "开始备份Redis..."
redis-cli BGSAVE
sleep 5
cp /var/lib/redis/dump.rdb "$BACKUP_DIR/redis/dump_$TIMESTAMP.rdb"
log "Redis备份完成 ✅"
}}
# 备份配置文件
backup_configs() {{
log "开始备份配置..."
tar czf "$BACKUP_DIR/config/configs_$TIMESTAMP.tar.gz" \
/etc/nginx/ /etc/ssh/ /opt/app/config/
log "配置备份完成 ✅"
}}
# 清理过期备份
cleanup() {{
log "清理${{RETENTION_DAYS}}天前的备份..."
find "$BACKUP_DIR" -type f -mtime +$RETENTION_DAYS -delete
log "清理完成 ✅"
}}
# 执行
backup_mysql
backup_redis
backup_configs
cleanup
log "========== 全部备份完成 =========="
开发提交代码
│
▼
┌──────────┐ Shell脚本
│ CI/CD │◄──────────────┐
│ Pipeline │ │
└────┬─────┘ │
│ 触发 │ 自动化
▼ │
┌──────────┐ ┌──────┐ │
│ 构建脚本 │ │ 部署 │ │
│ build.sh │ │脚本 │───┘
└────┬─────┘ │deploy│
│ │.sh │
▼ └──┬───┘
┌──────────┐ │
│ 测试脚本 │ ▼
│ test.sh │ ┌──────┐
└────┬─────┘ │ 服务器│
│ │ 集群 │
▼ └──────┘
┌──────────┐ ▲
│ 监控脚本 │ │
│ monitor │───────┘
│ .sh │ 健康检查
└──────────┘
# 原因:脚本没有执行权限
chmod +x deploy.sh
# 或者用bash显式执行
bash deploy.sh
# 检查shebang行
head -1 deploy.sh
#!/usr/bin/env bash # 推荐写法
# 如果是Windows换行符问题
file deploy.sh
# deploy.sh: ASCII text, with CRLF line terminators
sed -i 's/\r$//' deploy.sh # 修复
# 危险写法
rm -rf $DIR/* # 如果DIR为空,变成 rm -rf /* !!!
# 安全写法
rm -rf "$${{DIR:?未设置DIR变量}}"/*
# 使用set -u防止未定义变量
set -u
echo $UNDEFINED_VAR # 报错退出而不是输出空
# 默认值
DB_HOST="$${{DB_HOST:-localhost}}"
DB_PORT="$${{DB_PORT:-5432}}"
掌握Shell脚本编程、文本处理三剑客、自动化运维脚本编写——这是DevOps自动化的基石。