1. Crontab基础知识
1.1 什么是Crontab
Crontab是Linux系统中用于设置周期性执行任务的工具,它通过cron守护进程来运行预定的任务。这些任务通常称为cron jobs,可以按分钟、小时、日、月、星期等各种时间周期执行。
1.2 Crontab文件格式
Crontab文件包含一系列命令,每行一个任务,格式如下:
scss
* * * * * command_to_execute
- - - - -
| | | | |
| | | | +----- 星期几 (0 - 7) (星期天为0或7)
| | | +------- 月份 (1 - 12)
| | +--------- 日 (1 - 31)
| +----------- 小时 (0 - 23)
+------------- 分钟 (0 - 59)
1.3 特殊时间表达式
bash
# 特殊字符说明
* 任何值
, 值列表分隔符
- 范围值
/ 步长值
# 常用示例
*/5 * * * * # 每5分钟
0 * * * * # 每小时
0 0 * * * # 每天零点
0 0 * * 0 # 每周日零点
0 0 1 * * # 每月1号零点
2. Crontab基本操作
2.1 查看当前用户的Crontab
bash
# 查看当前crontab
crontab -l
# 查看其他用户的crontab(需要root权限)
crontab -u username -l
2.2 编辑Crontab
bash
# 编辑当前用户的crontab
crontab -e
# 编辑其他用户的crontab
crontab -u username -e
2.3 删除Crontab
bash
# 删除当前用户的所有cron任务
crontab -r
# 删除前确认(交互式)
crontab -i -r
2.4 导入Crontab文件
bash
# 从文件导入crontab
crontab cronfile.txt
# 备份当前crontab到文件
crontab -l > cron_backup.txt
3. Crontab环境配置
3.1 设置环境变量
在crontab文件开头设置环境变量:
bash
# 编辑crontab时添加环境变量
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=user@example.com
HOME=/home/username
# 定时任务示例
0 * * * * /path/to/script.sh
3.2 处理路径问题
由于cron执行环境与用户shell环境不同,建议在脚本中使用绝对路径:
bash
#!/bin/bash
# 在脚本中设置路径
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# 使用绝对路径执行命令
/usr/bin/touch /tmp/test.file
/bin/echo "任务执行时间: $(date)" >> /var/log/cron.log
4. 实用场景一:日志切割
4.1 日志切割脚本
创建日志切割脚本 /usr/local/bin/logrotate_custom.sh:
bash
#!/bin/bash
# 日志切割脚本
# 设置环境变量
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# 日志目录
LOG_DIR="/var/log/myapp"
BACKUP_DIR="/var/log/myapp/backup"
# 创建备份目录
mkdir -p "$BACKUP_DIR"
# 当前日期
DATE=$(date +%Y%m%d_%H%M%S)
# 应用程序日志文件
APP_LOGS=("application.log" "error.log" "access.log")
# 切割日志函数
rotate_log() {
local log_file=$1
local backup_file="${BACKUP_DIR}/$(basename ${log_file})_${DATE}.log"
if [ -f "$log_file" ]; then
# 备份原日志文件
cp "$log_file" "$backup_file"
# 清空原日志文件
> "$log_file"
# 压缩备份文件(后台执行)
gzip "$backup_file" &
echo "$(date): 已切割日志 $log_file -> ${backup_file}.gz" >> "${LOG_DIR}/rotate.log"
fi
}
# 主循环
for log in "${APP_LOGS[@]}"; do
log_path="${LOG_DIR}/${log}"
rotate_log "$log_path"
done
# 清理30天前的备份文件
find "$BACKUP_DIR" -name "*.gz" -mtime +30 -delete
echo "$(date): 日志切割完成" >> "${LOG_DIR}/rotate.log"
4.2 设置执行权限并测试
bash
# 给脚本执行权限
chmod +x /usr/local/bin/logrotate_custom.sh
# 创建测试日志目录
sudo mkdir -p /var/log/myapp/backup
sudo touch /var/log/myapp/{application.log,error.log,access.log,rotate.log}
# 测试脚本
/usr/local/bin/logrotate_custom.sh
4.3 添加Crontab任务
bash
# 编辑crontab
crontab -e
# 添加以下内容 - 每天凌晨2点执行日志切割
0 2 * * * /usr/local/bin/logrotate_custom.sh
# 也可以每小时执行一次(用于测试)
# 0 * * * * /usr/local/bin/logrotate_custom.sh
4.4 日志切割流程图
graph TD
A[开始日志切割] --> B[初始化环境变量和路径]
B --> C[创建备份目录]
C --> D[获取当前日期时间]
D --> E[遍历日志文件列表]
E --> F{检查日志文件是否存在}
F -->|是| G[备份原日志文件]
F -->|否| E
G --> H[清空原日志文件内容]
H --> I[压缩备份文件]
I --> J[记录操作日志]
J --> E
E -->|所有文件处理完成| K[清理30天前旧备份]
K --> L[记录完成日志]
L --> M[结束]
style A fill:#2d5c7f,color:white
style M fill:#2d5c7f,color:white
style B fill:#4a86e8,color:white
style C fill:#4a86e8,color:white
style D fill:#4a86e8,color:white
style E fill:#6d9eeb,color:white
style F fill:#9fc5e8,color:white
style G fill:#3d85c6,color:white
style H fill:#3d85c6,color:white
style I fill:#3d85c6,color:white
style J fill:#6fa8dc,color:white
style K fill:#e69138,color:white
style L fill:#e69138,color:white
5. 实用场景二:证书自动续期
5.1 Let's Encrypt证书自动续期
创建证书续期脚本 /usr/local/bin/cert_renewal.sh:
bash
#!/bin/bash
# SSL证书自动续期脚本
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# 日志文件
LOG_FILE="/var/log/cert_renewal.log"
CONFIG_DIR="/etc/letsencrypt"
WEBROOT="/var/www/certbot"
# 域名列表
DOMAINS=("example.com" "www.example.com")
# 邮箱地址(用于证书通知)
EMAIL="admin@example.com"
# 创建必要的目录
mkdir -p "$(dirname "$LOG_FILE")"
mkdir -p "$WEBROOT"
# 记录日志函数
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
}
# 检查证书是否快要过期
check_cert_expiry() {
local domain=$1
local cert_file="$CONFIG_DIR/live/$domain/fullchain.pem"
if [ ! -f "$cert_file" ]; then
echo "new"
return
fi
local expiry_date=$(openssl x509 -in "$cert_file" -noout -enddate | cut -d= -f2)
local expiry_epoch=$(date -d "$expiry_date" +%s)
local current_epoch=$(date +%s)
local days_until_expiry=$(( (expiry_epoch - current_epoch) / 86400 ))
if [ $days_until_expiry -lt 30 ]; then
echo "expiring"
else
echo "valid"
fi
}
# 获取证书函数
get_certificate() {
log "开始获取SSL证书"
# 使用certbot获取证书
if certbot certonly --webroot -w "$WEBROOT" \
-d "${DOMAINS[0]}" \
-d "${DOMAINS[1]}" \
--email "$EMAIL" \
--agree-tos \
--non-interactive \
--force-renewal >> "$LOG_FILE" 2>&1; then
log "SSL证书获取成功"
return 0
else
log "SSL证书获取失败"
return 1
fi
}
# 续期证书函数
renew_certificate() {
log "开始续期SSL证书"
if certbot renew --non-interactive >> "$LOG_FILE" 2>&1; then
log "SSL证书续期成功"
# 重新加载web服务器配置
reload_webserver
return 0
else
log "SSL证书续期失败"
return 1
fi
}
# 重新加载web服务器
reload_webserver() {
# 检测并重新加载web服务器
if systemctl is-active --quiet nginx; then
systemctl reload nginx
log "Nginx配置重新加载"
elif systemctl is-active --quiet apache2; then
systemctl reload apache2
log "Apache配置重新加载"
fi
}
# 主函数
main() {
log "=== 开始证书检查 ==="
local status=$(check_cert_expiry "${DOMAINS[0]}")
case "$status" in
"new")
log "未找到证书,开始申请新证书"
get_certificate
;;
"expiring")
log "证书即将过期,开始续期"
renew_certificate
;;
"valid")
log "证书有效,无需操作"
;;
esac
log "=== 证书检查完成 ==="
}
# 执行主函数
main "$@"
5.2 安装Certbot
bash
# Ubuntu/Debian系统
sudo apt update
sudo apt install certbot python3-certbot-nginx -y
# CentOS/RHEL系统
sudo yum install epel-release -y
sudo yum install certbot python3-certbot-nginx -y
# 创建webroot目录
sudo mkdir -p /var/www/certbot
sudo chown -R www-data:www-data /var/www/certbot # Ubuntu
# 或者
sudo chown -R nginx:nginx /var/www/certbot # CentOS
5.3 测试证书获取
bash
# 给脚本执行权限
chmod +x /usr/local/bin/cert_renewal.sh
# 手动测试(首次获取证书)
/usr/local/bin/cert_renewal.sh
# 查看日志
tail -f /var/log/cert_renewal.log
5.4 添加Crontab任务
bash
# 编辑crontab
crontab -e
# 添加以下内容 - 每天凌晨3点检查证书
0 3 * * * /usr/local/bin/cert_renewal.sh
# 每周一凌晨2点强制检查续期
0 2 * * 1 /usr/local/bin/cert_renewal.sh
5.5 证书续期流程图
flowchart TD
A[开始证书检查] --> B[初始化环境和路径]
B --> C[检查证书状态]
C --> D{证书状态判断}
D -->|证书不存在| E[申请新证书]
D -->|证书即将过期| F[续期现有证书]
D -->|证书有效| G[记录状态并退出]
E --> H{申请成功?}
H -->|是| I[记录成功日志]
H -->|否| J[记录失败日志]
F --> K{续期成功?}
K -->|是| L[重新加载Web服务器]
K -->|否| J
L --> I
I --> M[完成证书操作]
J --> M
style A fill:#2d5c7f,color:white
style M fill:#2d5c7f,color:white
style B fill:#4a86e8,color:white
style C fill:#4a86e8,color:white
style D fill:#6d9eeb,color:white
style E fill:#e69138,color:white
style F fill:#e69138,color:white
style G fill:#6aa84f,color:white
style H fill:#9fc5e8,color:white
style K fill:#9fc5e8,color:white
style I fill:#6aa84f,color:white
style J fill:#cc0000,color:white
style L fill:#3d85c6,color:white
6. 实用场景三:数据库自动备份
6.1 MySQL数据库备份脚本
创建MySQL备份脚本 /usr/local/bin/mysql_backup.sh:
bash
#!/bin/bash
# MySQL数据库自动备份脚本
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# 备份配置
BACKUP_DIR="/backup/mysql"
DATE=$(date +%Y%m%d_%H%M%S)
RETENTION_DAYS=30
# MySQL连接配置
MYSQL_HOST="localhost"
MYSQL_USER="backup_user"
MYSQL_PASSWORD="your_backup_password"
MYSQL_PORT="3306"
# 日志文件
LOG_FILE="/var/log/mysql_backup.log"
# 创建备份目录
mkdir -p "$BACKUP_DIR"
# 记录日志函数
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
}
# 备份单个数据库
backup_database() {
local db_name=$1
local backup_file="${BACKUP_DIR}/${db_name}_${DATE}.sql.gz"
log "开始备份数据库: $db_name"
if mysqldump -h "$MYSQL_HOST" -P "$MYSQL_PORT" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" \
--single-transaction \
--routines \
--triggers \
--events "$db_name" | gzip > "$backup_file"; then
log "数据库 $db_name 备份成功: $(du -h "$backup_file" | cut -f1)"
return 0
else
log "数据库 $db_name 备份失败"
return 1
fi
}
# 获取所有数据库列表
get_databases() {
mysql -h "$MYSQL_HOST" -P "$MYSQL_PORT" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" \
-e "SHOW DATABASES;" -s --skip-column-names | \
grep -Ev "(information_schema|performance_schema|mysql|sys)"
}
# 清理旧备份
cleanup_old_backups() {
log "开始清理 $RETENTION_DAYS 天前的旧备份"
local deleted_count=0
while IFS= read -r -d '' file; do
rm -- "$file"
((deleted_count++))
log "删除旧备份: $(basename "$file")"
done < <(find "$BACKUP_DIR" -name "*.sql.gz" -mtime +$RETENTION_DAYS -print0)
log "清理完成,共删除 $deleted_count 个旧备份文件"
}
# 验证备份文件
verify_backup() {
local backup_file=$1
local db_name=$2
log "验证备份文件: $(basename "$backup_file")"
if gzip -t "$backup_file" 2>/dev/null; then
log "备份文件验证成功: $(basename "$backup_file")"
return 0
else
log "备份文件验证失败: $(basename "$backup_file")"
# 删除损坏的备份文件
rm -f "$backup_file"
return 1
fi
}
# 主函数
main() {
log "=== 开始MySQL数据库备份 ==="
# 检查MySQL连接
if ! mysql -h "$MYSQL_HOST" -P "$MYSQL_PORT" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" -e "SELECT 1;" >/dev/null 2>&1; then
log "错误: 无法连接到MySQL服务器"
exit 1
fi
local total_dbs=0
local success_dbs=0
# 备份所有数据库
while IFS= read -r db; do
if [ -n "$db" ]; then
((total_dbs++))
if backup_database "$db"; then
local backup_file="${BACKUP_DIR}/${db}_${DATE}.sql.gz"
if verify_backup "$backup_file" "$db"; then
((success_dbs++))
fi
fi
fi
done < <(get_databases)
log "数据库备份完成: 成功 $success_dbs/$total_dbs"
# 清理旧备份
cleanup_old_backups
log "=== MySQL数据库备份完成 ==="
}
# 执行主函数
main "$@"
6.2 创建MySQL备份用户
sql
-- 登录MySQL
mysql -u root -p
-- 创建备份用户
CREATE USER 'backup_user'@'localhost' IDENTIFIED BY 'strong_password_here';
-- 授予备份权限
GRANT SELECT, RELOAD, LOCK TABLES, REPLICATION CLIENT, SHOW VIEW, EVENT, TRIGGER
ON *.* TO 'backup_user'@'localhost';
-- 刷新权限
FLUSH PRIVILEGES;
-- 退出
EXIT;
6.3 设置脚本权限和测试
bash
# 给脚本执行权限
chmod +x /usr/local/bin/mysql_backup.sh
# 创建备份目录
sudo mkdir -p /backup/mysql
sudo chown -R $(whoami):$(whoami) /backup/mysql
# 创建日志目录
sudo mkdir -p /var/log/
sudo touch /var/log/mysql_backup.log
sudo chown $(whoami):$(whoami) /var/log/mysql_backup.log
# 测试备份脚本
/usr/local/bin/mysql_backup.sh
# 查看备份文件
ls -la /backup/mysql/
6.4 添加Crontab任务
bash
# 编辑crontab
crontab -e
# 添加以下内容 - 每天凌晨1点执行数据库备份
0 1 * * * /usr/local/bin/mysql_backup.sh
# 每周日凌晨2点执行完整备份
0 2 * * 0 /usr/local/bin/mysql_backup.sh
7. 实用场景四:系统监控与告警
7.1 系统监控脚本
创建系统监控脚本 /usr/local/bin/system_monitor.sh:
bash
#!/bin/bash
# 系统监控与告警脚本
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# 配置参数
ALERT_THRESHOLD_CPU=80
ALERT_THRESHOLD_MEMORY=85
ALERT_THRESHOLD_DISK=90
LOG_FILE="/var/log/system_monitor.log"
ALERT_EMAIL="admin@example.com"
# 监控目录(可配置多个)
MONITOR_DIRS=("/" "/home" "/var" "/opt")
# 记录日志函数
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
}
# 发送邮件告警
send_alert() {
local subject=$1
local message=$2
echo "$message" | mail -s "$subject" "$ALERT_EMAIL"
log "告警已发送: $subject"
}
# 检查CPU使用率
check_cpu_usage() {
local cpu_usage=$(top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{print 100 - $1}')
local cpu_usage_int=$(printf "%.0f" "$cpu_usage")
echo "CPU使用率: ${cpu_usage}%"
if [ "$cpu_usage_int" -gt "$ALERT_THRESHOLD_CPU" ]; then
send_alert "CPU使用率告警" "CPU使用率过高: ${cpu_usage}% (阈值: ${ALERT_THRESHOLD_CPU}%)"
fi
}
# 检查内存使用率
check_memory_usage() {
local memory_info=$(free | grep Mem)
local total_memory=$(echo "$memory_info" | awk '{print $2}')
local used_memory=$(echo "$memory_info" | awk '{print $3}')
local memory_usage=$(( used_memory * 100 / total_memory ))
echo "内存使用率: ${memory_usage}%"
if [ "$memory_usage" -gt "$ALERT_THRESHOLD_MEMORY" ]; then
send_alert "内存使用率告警" "内存使用率过高: ${memory_usage}% (阈值: ${ALERT_THRESHOLD_MEMORY}%)"
fi
}
# 检查磁盘使用率
check_disk_usage() {
for dir in "${MONITOR_DIRS[@]}"; do
local disk_usage=$(df -h "$dir" | awk 'NR==2 {print $5}' | sed 's/%//')
echo "磁盘 $dir 使用率: ${disk_usage}%"
if [ "$disk_usage" -gt "$ALERT_THRESHOLD_DISK" ]; then
send_alert "磁盘空间告警" "磁盘 $dir 使用率过高: ${disk_usage}% (阈值: ${ALERT_THRESHOLD_DISK}%)"
fi
done
}
# 检查系统负载
check_system_load() {
local load_avg=$(cat /proc/loadavg | awk '{print $1}')
local cpu_cores=$(nproc)
local load_threshold=$(( cpu_cores * 2 ))
echo "系统负载: $load_avg (CPU核心数: $cpu_cores)"
if (( $(echo "$load_avg > $load_threshold" | bc -l) )); then
send_alert "系统负载告警" "系统负载过高: ${load_avg} (CPU核心数: ${cpu_cores})"
fi
}
# 检查关键服务状态
check_services() {
local services=("nginx" "mysql" "ssh" "cron")
for service in "${services[@]}"; do
if systemctl is-active --quiet "$service"; then
echo "服务 $service: 运行中"
else
echo "服务 $service: 停止"
send_alert "服务状态告警" "关键服务 $service 已停止"
fi
done
}
# 生成监控报告
generate_report() {
local report_file="/tmp/system_report_$(date +%Y%m%d_%H%M%S).txt"
{
echo "=== 系统监控报告 $(date) ==="
echo "主机名: $(hostname)"
echo "运行时间: $(uptime -p)"
echo "---"
check_cpu_usage
check_memory_usage
check_disk_usage
check_system_load
echo "---"
check_services
echo "=== 报告结束 ==="
} > "$report_file"
echo "监控报告已生成: $report_file"
cat "$report_file" >> "$LOG_FILE"
}
# 主函数
main() {
log "开始系统监控检查"
generate_report
log "系统监控检查完成"
}
# 执行主函数
main "$@"
7.2 安装邮件工具
bash
# Ubuntu/Debian系统
sudo apt update
sudo apt install mailutils -y
# CentOS/RHEL系统
sudo yum install mailx -y
7.3 设置脚本权限和测试
bash
# 给脚本执行权限
chmod +x /usr/local/bin/system_monitor.sh
# 创建日志文件
sudo touch /var/log/system_monitor.log
sudo chown $(whoami):$(whoami) /var/log/system_monitor.log
# 测试监控脚本
/usr/local/bin/system_monitor.sh
# 查看监控报告
cat /var/log/system_monitor.log
7.4 添加Crontab任务
bash
# 编辑crontab
crontab -e
# 添加以下内容 - 每5分钟执行一次系统监控
*/5 * * * * /usr/local/bin/system_monitor.sh
# 每天8点生成详细报告
0 8 * * * /usr/local/bin/system_monitor.sh
8. 实用场景五:文件同步与备份
8.1 文件同步脚本
创建文件同步脚本 /usr/local/bin/file_sync.sh:
bash
#!/bin/bash
# 文件同步与备份脚本
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# 同步配置
SOURCE_DIRS=("/home/user/documents" "/home/user/photos")
BACKUP_DIR="/backup/files"
REMOTE_USER="user"
REMOTE_HOST="backup.server.com"
REMOTE_DIR="/backup/$(hostname)"
LOG_FILE="/var/log/file_sync.log"
RETENTION_DAYS=30
# 记录日志函数
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
}
# 本地备份函数
local_backup() {
local timestamp=$(date +%Y%m%d_%H%M%S)
local backup_path="${BACKUP_DIR}/local_${timestamp}"
log "开始本地备份"
mkdir -p "$backup_path"
for dir in "${SOURCE_DIRS[@]}"; do
if [ -d "$dir" ]; then
local dir_name=$(basename "$dir")
log "备份目录: $dir -> ${backup_path}/${dir_name}"
if rsync -av --delete "$dir" "$backup_path/" >> "$LOG_FILE" 2>&1; then
log "目录 $dir 备份成功"
else
log "目录 $dir 备份失败"
fi
else
log "警告: 目录 $dir 不存在"
fi
done
log "本地备份完成: $backup_path"
}
# 远程同步函数
remote_sync() {
log "开始远程同步"
# 检查SSH连接
if ! ssh -o BatchMode=yes -o ConnectTimeout=5 "${REMOTE_USER}@${REMOTE_HOST}" "echo 'SSH连接成功'" >> "$LOG_FILE" 2>&1; then
log "错误: 无法建立SSH连接到 ${REMOTE_HOST}"
return 1
fi
# 创建远程目录
ssh "${REMOTE_USER}@${REMOTE_HOST}" "mkdir -p ${REMOTE_DIR}"
# 同步每个目录
for dir in "${SOURCE_DIRS[@]}"; do
if [ -d "$dir" ]; then
log "同步目录: $dir -> ${REMOTE_HOST}:${REMOTE_DIR}"
if rsync -avz --delete -e ssh "$dir" "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DIR}/" >> "$LOG_FILE" 2>&1; then
log "目录 $dir 同步成功"
else
log "目录 $dir 同步失败"
fi
fi
done
log "远程同步完成"
}
# 清理旧备份
cleanup_backups() {
log "开始清理本地旧备份"
local deleted_count=0
while IFS= read -r -d '' dir; do
rm -rf "$dir"
((deleted_count++))
log "删除旧备份: $(basename "$dir")"
done < <(find "$BACKUP_DIR" -name "local_*" -type d -mtime +$RETENTION_DAYS -print0)
log "本地备份清理完成,共删除 $deleted_count 个目录"
}
# 验证备份完整性
verify_backup() {
local backup_path=$1
log "验证备份完整性: $backup_path"
for dir in "${SOURCE_DIRS[@]}"; do
if [ -d "$dir" ]; then
local dir_name=$(basename "$dir")
local source_count=$(find "$dir" -type f | wc -l)
local backup_count=$(find "${backup_path}/${dir_name}" -type f 2>/dev/null | wc -l)
if [ "$source_count" -eq "$backup_count" ]; then
log "验证成功: $dir_name ($source_count 个文件)"
else
log "验证失败: $dir_name (源: $source_count, 备份: $backup_count)"
fi
fi
done
}
# 生成同步报告
generate_sync_report() {
local report_file="/tmp/sync_report_$(date +%Y%m%d_%H%M%S).txt"
{
echo "=== 文件同步报告 $(date) ==="
echo "主机名: $(hostname)"
echo "同步时间: $(date)"
echo "---"
echo "同步目录:"
for dir in "${SOURCE_DIRS[@]}"; do
if [ -d "$dir" ]; then
local file_count=$(find "$dir" -type f | wc -l)
local total_size=$(du -sh "$dir" | cut -f1)
echo " - $dir ($file_count 个文件, 总大小: $total_size)"
else
echo " - $dir (目录不存在)"
fi
done
echo "---"
echo "备份位置:"
echo " - 本地: $BACKUP_DIR"
echo " - 远程: ${REMOTE_HOST}:${REMOTE_DIR}"
echo "=== 报告结束 ==="
} > "$report_file"
log "同步报告已生成: $report_file"
}
# 主函数
main() {
local mode=${1:-"all"} # all, local, remote
log "开始文件同步任务 (模式: $mode)"
case "$mode" in
"local")
local_backup
;;
"remote")
remote_sync
;;
"all")
local_backup
remote_sync
;;
*)
log "错误: 未知模式 $mode"
exit 1
;;
esac
# 验证最新备份
local latest_backup=$(find "$BACKUP_DIR" -name "local_*" -type d -printf '%T@ %p\n' 2>/dev/null | sort -n | tail -1 | cut -d' ' -f2-)
if [ -n "$latest_backup" ]; then
verify_backup "$latest_backup"
fi
# 清理旧备份
cleanup_backups
# 生成报告
generate_sync_report
log "文件同步任务完成"
}
# 执行主函数
main "$@"
8.2 设置SSH密钥认证
bash
# 生成SSH密钥对(如果还没有)
ssh-keygen -t rsa -b 4096 -C "$(hostname)-backup" -f ~/.ssh/backup_key
# 将公钥复制到远程服务器
ssh-copy-id -i ~/.ssh/backup_key.pub user@backup.server.com
# 测试SSH连接
ssh -i ~/.ssh/backup_key user@backup.server.com "echo '连接成功'"
8.3 设置脚本权限和测试
bash
# 给脚本执行权限
chmod +x /usr/local/bin/file_sync.sh
# 创建备份目录
sudo mkdir -p /backup/files
sudo chown -R $(whoami):$(whoami) /backup
# 创建日志文件
sudo touch /var/log/file_sync.log
sudo chown $(whoami):$(whoami) /var/log/file_sync.log
# 测试本地备份
/usr/local/bin/file_sync.sh local
# 测试完整同步(需要配置远程服务器)
# /usr/local/bin/file_sync.sh all
8.4 添加Crontab任务
bash
# 编辑crontab
crontab -e
# 添加以下内容 - 每天凌晨4点执行完整同步
0 4 * * * /usr/local/bin/file_sync.sh all
# 每小时执行本地备份
0 * * * * /usr/local/bin/file_sync.sh local
9. 实用场景六:网站内容更新
9.1 网站自动更新脚本
创建网站更新脚本 /usr/local/bin/website_update.sh:
bash
#!/bin/bash
# 网站内容自动更新脚本
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# 网站配置
WEBSITE_DIR="/var/www/html"
BACKUP_DIR="/backup/website"
GIT_REPO="https://github.com/username/website.git"
LOG_FILE="/var/log/website_update.log"
# 通知配置
SLACK_WEBHOOK="https://hooks.slack.com/services/your/webhook/url"
EMAIL_RECIPIENT="admin@example.com"
# 记录日志函数
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" # 同时输出到控制台
}
# 发送Slack通知
send_slack_notification() {
local message=$1
if [ -n "$SLACK_WEBHOOK" ]; then
curl -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"$message\"}" \
"$SLACK_WEBHOOK" >/dev/null 2>&1 || log "Slack通知发送失败"
fi
}
# 发送邮件通知
send_email_notification() {
local subject=$1
local body=$2
echo "$body" | mail -s "$subject" "$EMAIL_RECIPIENT"
log "邮件通知已发送: $subject"
}
# 备份当前网站
backup_website() {
local timestamp=$(date +%Y%m%d_%H%M%S)
local backup_path="${BACKUP_DIR}/${timestamp}"
log "开始备份当前网站"
mkdir -p "$backup_path"
if [ -d "$WEBSITE_DIR" ]; then
if cp -r "$WEBSITE_DIR"/* "$backup_path"/ 2>/dev/null; then
log "网站备份成功: $backup_path"
echo "$backup_path"
return 0
else
log "网站备份失败"
return 1
fi
else
log "错误: 网站目录 $WEBSITE_DIR 不存在"
return 1
fi
}
# 检查Git仓库状态
check_git_status() {
if [ ! -d "${WEBSITE_DIR}/.git" ]; then
log "错误: $WEBSITE_DIR 不是Git仓库"
return 1
fi
cd "$WEBSITE_DIR" || return 1
# 检查是否有未提交的更改
if ! git diff-index --quiet HEAD --; then
log "检测到未提交的更改,创建临时提交"
git add .
git commit -m "自动备份 $(date '+%Y-%m-%d %H:%M:%S')"
fi
# 获取当前提交ID
local current_commit=$(git rev-parse --short HEAD)
log "当前提交: $current_commit"
echo "$current_commit"
}
# 从Git更新网站
update_from_git() {
log "开始从Git更新网站"
cd "$WEBSITE_DIR" || return 1
# 拉取最新更改
if git pull origin main; then
local new_commit=$(git rev-parse --short HEAD)
log "Git更新成功: $new_commit"
# 记录变更
local changes=$(git log --oneline HEAD~1..HEAD)
log "变更内容:\n$changes"
echo "$changes"
return 0
else
log "Git更新失败"
return 1
fi
}
# 克隆Git仓库(首次使用)
clone_git_repo() {
log "首次克隆Git仓库"
# 备份原有目录
if [ -d "$WEBSITE_DIR" ]; then
local backup_path=$(backup_website)
if [ $? -eq 0 ]; then
log "原网站已备份到: $backup_path"
fi
# 清空目录
rm -rf "${WEBSITE_DIR:?}/"*
else
mkdir -p "$WEBSITE_DIR"
fi
# 克隆仓库
if git clone "$GIT_REPO" "$WEBSITE_DIR"; then
log "Git仓库克隆成功"
return 0
else
log "Git仓库克隆失败"
return 1
fi
}
# 修复文件权限
fix_permissions() {
log "修复文件权限"
# 设置正确的所有权和权限
if getent group www-data >/dev/null; then
chown -R www-data:www-data "$WEBSITE_DIR"
elif getent group nginx >/dev/null; then
chown -R nginx:nginx "$WEBSITE_DIR"
elif getent group apache >/dev/null; then
chown -R apache:apache "$WEBSITE_DIR"
fi
# 设置目录权限为755,文件权限为644
find "$WEBSITE_DIR" -type d -exec chmod 755 {} \;
find "$WEBSITE_DIR" -type f -exec chmod 644 {} \;
# 设置可执行文件的权限
find "$WEBSITE_DIR" -name "*.sh" -exec chmod +x {} \;
log "文件权限修复完成"
}
# 测试网站功能
test_website() {
local website_url="http://localhost"
log "测试网站功能: $website_url"
# 使用curl测试网站可访问性
if curl -f -s -o /dev/null --connect-timeout 10 "$website_url"; then
log "网站测试成功"
return 0
else
log "网站测试失败"
return 1
fi
}
# 回滚到上一个版本
rollback_website() {
local backup_path=$1
local reason=$2
log "开始回滚网站: $reason"
if [ -d "$backup_path" ]; then
# 清空当前目录
rm -rf "${WEBSITE_DIR:?}/"*
# 恢复备份
if cp -r "$backup_path"/* "$WEBSITE_DIR"/; then
log "网站回滚成功"
fix_permissions
return 0
else
log "网站回滚失败"
return 1
fi
else
log "错误: 备份目录 $backup_path 不存在"
return 1
fi
}
# 主函数
main() {
local action=${1:-"update"} # update, clone, rollback
log "=== 开始网站更新任务 ==="
case "$action" in
"clone")
if clone_git_repo; then
fix_permissions
send_slack_notification "网站初始化完成: $(hostname)"
else
send_slack_notification "网站初始化失败: $(hostname)"
exit 1
fi
;;
"update")
# 创建备份
local backup_path=$(backup_website)
if [ $? -ne 0 ]; then
log "备份失败,中止更新"
exit 1
fi
# 检查Git状态
local old_commit=$(check_git_status)
# 更新网站
local changes=$(update_from_git)
if [ $? -eq 0 ]; then
fix_permissions
# 测试网站
if test_website; then
local message="网站更新成功: $(hostname)\n最新提交: $(git -C "$WEBSITE_DIR" rev-parse --short HEAD)\n变更内容:\n$changes"
send_slack_notification "$message"
log "网站更新完成"
else
log "网站测试失败,执行回滚"
if rollback_website "$backup_path" "更新后测试失败"; then
send_slack_notification "网站更新失败已回滚: $(hostname)"
else
send_slack_notification "网站更新失败且回滚也失败: $(hostname) - 需要手动干预"
fi
fi
else
log "更新失败,执行回滚"
if rollback_website "$backup_path" "更新过程失败"; then
send_slack_notification "网站更新失败已回滚: $(hostname)"
else
send_slack_notification "网站更新失败且回滚也失败: $(hostname) - 需要手动干预"
fi
fi
;;
*)
log "错误: 未知操作 $action"
exit 1
;;
esac
log "=== 网站更新任务完成 ==="
}
# 执行主函数
main "$@"
9.2 首次设置Git仓库
bash
# 给脚本执行权限
chmod +x /usr/local/bin/website_update.sh
# 创建备份目录
sudo mkdir -p /backup/website
sudo chown -R $(whoami):$(whoami) /backup
# 创建日志文件
sudo touch /var/log/website_update.log
sudo chown $(whoami):$(whoami) /var/log/website_update.log
# 如果网站目录不存在,创建它
sudo mkdir -p /var/www/html
sudo chown -R $(whoami):$(whoami) /var/www/html
# 首次克隆仓库(需要先配置GIT_REPO变量)
/usr/local/bin/website_update.sh clone
9.3 添加Crontab任务
bash
# 编辑crontab
crontab -e
# 添加以下内容 - 每15分钟检查更新
*/15 * * * * /usr/local/bin/website_update.sh update
# 每天凌晨5点强制更新
0 5 * * * /usr/local/bin/website_update.sh update
10. 其他实用场景简要说明
10.1 场景七:清理临时文件
bash
#!/bin/bash
# 清理临时文件脚本
find /tmp -type f -atime +7 -delete
find /var/tmp -type f -atime +30 -delete
# 添加到crontab: 0 3 * * * /path/to/cleanup_script.sh
10.2 场景八:监控网站可用性
bash
#!/bin/bash
# 网站监控脚本
if ! curl -f -s --max-time 10 "https://yoursite.com" > /dev/null; then
echo "网站不可用" | mail -s "网站宕机告警" admin@example.com
fi
# 添加到crontab: */5 * * * * /path/to/website_monitor.sh
10.3 场景九:自动安全更新
bash
#!/bin/bash
# 安全更新脚本
apt update && apt list --upgradable
# 添加到crontab: 0 2 * * 6 /path/to/security_update.sh
10.4 场景十:数据统计报告
bash
#!/bin/bash
# 数据统计脚本
echo "系统统计报告" > /tmp/report.txt
df -h >> /tmp/report.txt
# 添加到crontab: 0 9 * * 1 /path/to/stats_report.sh
11. Crontab高级技巧与最佳实践
11.1 错误处理与日志记录
bash
# 在crontab中正确记录日志
0 * * * * /path/to/script.sh >> /var/log/cron_script.log 2>&1
# 使用logger记录到系统日志
0 * * * * /path/to/script.sh 2>&1 | logger -t cron_script
# 带有错误通知的配置
0 * * * * /path/to/script.sh || echo "任务执行失败" | mail -s "Cron任务失败" admin@example.com
11.2 任务锁定机制
创建任务锁定脚本 /usr/local/bin/with_lock.sh:
bash
#!/bin/bash
# 任务锁定脚本,防止重复执行
LOCK_FILE="/tmp/$1.lock"
if [ -f "$LOCK_FILE" ]; then
echo "任务正在执行中,跳过本次执行"
exit 1
fi
# 创建锁文件
touch "$LOCK_FILE"
# 执行实际任务
"$@"
# 删除锁文件
rm -f "$LOCK_FILE"
使用方式:
bash
# 在crontab中使用锁机制
*/5 * * * * /usr/local/bin/with_lock.sh /usr/local/bin/system_monitor.sh
11.3 环境隔离
bash
#!/bin/bash
# 环境隔离脚本示例
# 设置安全路径
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
# 设置umask
umask 022
# 切换到工作目录
cd /path/to/working/directory || exit 1
# 加载环境变量
if [ -f .env ]; then
set -a
source .env
set +a
fi
# 执行实际任务
./your_script.sh
11.4 Crontab管理流程图
flowchart TD
A[开始Crontab管理] --> B[分析任务需求]
B --> C[确定执行频率]
C --> D[编写执行脚本]
D --> E[设置环境变量]
E --> F[配置错误处理]
F --> G[添加任务锁定]
G --> H[测试脚本功能]
H --> I{测试是否通过?}
I -->|是| J[添加到Crontab]
I -->|否| K[调试修复脚本]
K --> D
J --> L[配置日志监控]
L --> M[设置告警通知]
M --> N[定期审查任务]
N --> O[优化性能]
O --> P[文档化配置]
P --> Q[完成]
style A fill:#2d5c7f,color:white
style Q fill:#2d5c7f,color:white
style B fill:#4a86e8,color:white
style C fill:#4a86e8,color:white
style D fill:#6d9eeb,color:white
style E fill:#6d9eeb,color:white
style F fill:#6d9eeb,color:white
style G fill:#6d9eeb,color:white
style H fill:#9fc5e8,color:white
style I fill:#e69138,color:white
style J fill:#6aa84f,color:white
style K fill:#cc0000,color:white
style L fill:#3d85c6,color:white
style M fill:#3d85c6,color:white
style N fill:#3d85c6,color:white
style O fill:#3d85c6,color:white
style P fill:#3d85c6,color:white
12. 故障排查与调试
12.1 常见问题排查
bash
# 检查cron服务状态
sudo systemctl status cron
sudo systemctl status crond # CentOS/RHEL
# 查看系统日志中的cron记录
sudo grep CRON /var/log/syslog
sudo grep crond /var/log/messages # CentOS/RHEL
# 检查用户cron权限
sudo cat /etc/cron.allow
sudo cat /etc/cron.deny
# 调试cron任务执行
# 在脚本开头添加详细日志
echo "$(date): 脚本开始执行" >> /tmp/debug.log
env >> /tmp/debug.log
12.2 测试Cron任务
bash
# 手动运行cron任务
/usr/local/bin/your_script.sh
# 查看执行结果
echo $?
# 测试cron环境
crontab -e
# 添加测试任务
* * * * * env > /tmp/cron_env.txt
12.3 性能监控
bash
# 监控cron任务执行时间
#!/bin/bash
START_TIME=$(date +%s)
# 你的任务代码在这里
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
echo "任务执行时间: ${DURATION}秒" >> /var/log/cron_performance.log
if [ $DURATION -gt 300 ]; then
echo "警告: 任务执行时间过长" | mail -s "性能告警" admin@example.com
fi
通过本教程,您应该已经掌握了Crontab定时任务的全面知识,从基础操作到高级应用场景。这些技能将帮助您自动化日常运维任务,提高工作效率和系统可靠性。记得在实际生产环境中充分测试所有脚本,并根据具体需求进行调整优化。