前言

Shell 脚本是运维工程师最常用的自动化工具。本文记录几个实战中常用的脚本模式。

日志定期清理

服务器日志堆积是常见问题,以下脚本按保留天数自动清理:

#!/bin/bash
# clean_logs.sh - 日志清理脚本
LOG_DIR="/var/log/app"
KEEP_DAYS=30

find "$LOG_DIR" -name "*.log" -type f -mtime +${KEEP_DAYS} -exec gzip {} \;
find "$LOG_DIR" -name "*.gz" -type f -mtime +${KEEP_DAYS} -delete

echo "$(date): 日志清理完成,保留 ${KEEP_DAYS} 天"

配合 crontab 每天执行:

0 2 * * * /opt/scripts/clean_logs.sh >> /var/log/clean_logs.log 2>&1

批量主机健康检查

通过 SSH 批量检查多台服务器状态:

#!/bin/bash
# health_check.sh - 批量健康检查
HOSTS=("web-01" "web-02" "db-01" "cache-01")
THRESHOLD=80

for host in "${HOSTS[@]}"; do
    echo "--- ${host} ---"
    ssh "$host" "
        echo \"CPU: \$(top -bn1 | grep 'Cpu' | awk '{print \$2}')%\"
        echo \"MEM: \$(free | awk '/Mem/{printf \"%.0f\", \$3/\$2*100}')%\"
        echo \"DISK: \$(df -h / | awk 'NR==2{print \$5}')\"
    " 2>/dev/null || echo "连接失败"
done

服务自动重启

检测服务异常并自动恢复:

#!/bin/bash
# auto_restart.sh - 服务自动重启
SERVICE="nginx"
MAX_RETRY=3

for i in $(seq 1 $MAX_RETRY); do
    if systemctl is-active --quiet "$SERVICE"; then
        exit 0
    fi
    echo "$(date): ${SERVICE} 未运行,尝试重启 (第 ${i} 次)"
    systemctl restart "$SERVICE"
    sleep 5
done

# 重试失败,发送告警
echo "$(date): ${SERVICE} 重启失败,需要人工介入" | mail -s "告警:${SERVICE} 异常" admin@example.com

配置文件备份

定期备份关键配置并带时间戳:

#!/bin/bash
# backup_conf.sh - 配置备份
BACKUP_DIR="/data/backup/config"
DATE=$(date +%Y%m%d_%H%M%S)
CONFIGS=(
    "/etc/nginx/nginx.conf"
    "/etc/ssh/sshd_config"
    "/etc/sysctl.conf"
)

mkdir -p "$BACKUP_DIR"
for conf in "${CONFIGS[@]}"; do
    if [ -f "$conf" ]; then
        cp "$conf" "${BACKUP_DIR}/$(basename $conf).${DATE}"
    fi
done

# 保留最近30天备份
find "$BACKUP_DIR" -type f -mtime +30 -delete

脚本编写规范

良好的习惯能减少踩坑:

  1. 开头加 set -euo pipefail:遇到错误即退出,未定义变量报错
  2. 变量加引号"$VAR" 防止空格和通配符问题
  3. 关键操作加日志echo "$(date): xxx" 方便排查
  4. 锁机制防重复执行flock 或 PID 文件
  5. 退出码规范:成功返回 0,失败返回非 0
#!/bin/bash
set -euo pipefail

LOCK_FILE="/tmp/$(basename $0).lock"
exec 200>"$LOCK_FILE"
flock -n 200 || { echo "脚本已在运行"; exit 1; }

# 业务逻辑...

总结

Shell 脚本虽然简单,但良好的编写习惯能大幅减少运维事故。核心原则:能自动化的不手动,能加检查的不裸跑