Crontab定时任务从入门到精通:教你如何实现日志切割、证书自动续期等十大实用场景

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定时任务的全面知识,从基础操作到高级应用场景。这些技能将帮助您自动化日常运维任务,提高工作效率和系统可靠性。记得在实际生产环境中充分测试所有脚本,并根据具体需求进行调整优化。

相关推荐
叽里咕噜怪7 小时前
VMware-三种网络模式
linux·运维·服务器
艾莉丝努力练剑8 小时前
【Linux权限 (二)】Linux权限机制深度解析:umask如何决定默认权限与粘滞位的妙用
大数据·linux·服务器·c++·ubuntu·centos·1024程序员节
稚辉君.MCA_P8_Java9 小时前
Java 基本数据类型 - 四类八种
java·linux·后端·mysql·架构
东木君_9 小时前
芯外拾遗第二篇:编译、工具链、烧录,你真的搞懂了吗?
linux·单片机·操作系统·嵌入式
虚伪的空想家9 小时前
HUAWEI A800I A2 aarch64架构Ubuntu服务器鲲鹏920开启 IOMMU/SMMU 硬件虚拟化功能
linux·服务器·ubuntu
赖small强9 小时前
[Linux] 内核链表实现详解
linux·内核链表·双向循环链表·list.h·list_head
Linux技术芯9 小时前
浅谈kswapd按照什么原则来换出页面的底层原理
linux
獭.獭.9 小时前
Linux -- 线程控制
linux·pthread·线程分离·线程取消·线程局部存储·lwp·线程栈
feng_blog66889 小时前
环形缓冲区实现共享内存
linux·c++