前言
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
脚本编写规范
良好的习惯能减少踩坑:
- 开头加
set -euo pipefail:遇到错误即退出,未定义变量报错 - 变量加引号:
"$VAR"防止空格和通配符问题 - 关键操作加日志:
echo "$(date): xxx"方便排查 - 锁机制防重复执行:
flock或 PID 文件 - 退出码规范:成功返回 0,失败返回非 0
#!/bin/bash
set -euo pipefail
LOCK_FILE="/tmp/$(basename $0).lock"
exec 200>"$LOCK_FILE"
flock -n 200 || { echo "脚本已在运行"; exit 1; }
# 业务逻辑...
总结
Shell 脚本虽然简单,但良好的编写习惯能大幅减少运维事故。核心原则:能自动化的不手动,能加检查的不裸跑。