#!/bin/bash # =========================================== # 学生状态变更处理脚本控制程序 - 生产环境版本 # 用于启动、停止、重启处理脚本 (Ubuntu兼容性修复版本) # =========================================== # # 生产环境部署指南: # 1. 确保所有修复版本脚本存在: # - process_student_changes_fixed.sh (状态变更处理) # - process_student_resume_fixed.sh (复学处理) # # 2. 确保配置文件存在: # - data.conf (数据库配置和脚本参数) # # 3. 启动所有服务: # ./control_scripts.sh startall # # 4. 监控运行状态: # ./control_scripts.sh statusall # # 5. 健康检查: # ./control_scripts.sh health all # # 6. 查看系统资源: # ./control_scripts.sh info # # 日志文件位置: # - 控制脚本日志: logs/control_scripts_YYYYMMDD.log # - 处理脚本日志: student_status_change/process_student_*_YYYYMMDD_HHMMSS.log # Disable strict error checking for better production resilience # set -euo pipefail # Temporarily disabled for production robustness # Force C locale for consistent behavior across systems export LANG=C export LC_ALL=C # Load configuration SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" CONFIG_FILE="${SCRIPT_DIR}/data.conf" if [[ ! -f "$CONFIG_FILE" ]]; then echo "错误:配置文件 $CONFIG_FILE 未找到!" echo "请确保 data.conf 文件存在于脚本目录中" exit 1 fi source "$CONFIG_FILE" # Production environment setup PRODUCTION_LOG_DIR="${SCRIPT_DIR}/logs" mkdir -p "$PRODUCTION_LOG_DIR" PRODUCTION_LOG_FILE="${PRODUCTION_LOG_DIR}/control_scripts_$(date +%Y%m%d).log" # Script definitions - Production versions with Ubuntu compatibility fixes # Using indexed arrays for better bash compatibility SCRIPT_NAMES=("changes" "resume") SCRIPT_FILES=("process_student_changes_fixed.sh" "process_student_resume_fixed.sh") # Function to get script file by name get_script_file() { local script_name="$1" for i in "${!SCRIPT_NAMES[@]}"; do if [[ "${SCRIPT_NAMES[$i]}" == "$script_name" ]]; then echo "${SCRIPT_FILES[$i]}" return 0 fi done return 1 } # PID files PID_DIR="${SCRIPT_DIR}/pids" mkdir -p "$PID_DIR" # Functions log() { local level="$1" local message="$2" local timestamp=$(date '+%Y-%m-%d %H:%M:%S') local log_message="[$timestamp] [$level] $message" # Output to console echo "$log_message" # Output to production log file echo "$log_message" >> "$PRODUCTION_LOG_FILE" } get_pid_file() { local script_name="$1" echo "${PID_DIR}/${script_name}.pid" } get_lock_file() { local script_name="$1" echo "${SCRIPT_DIR}/${script_name}.lock" } is_running() { local script_name="$1" local pid_file=$(get_pid_file "$script_name") local lock_file=$(get_lock_file "$script_name") # Check PID file if [[ -f "$pid_file" ]]; then local pid=$(cat "$pid_file") if kill -0 "$pid" 2>/dev/null; then return 0 # Running else log "WARN" "删除脚本 $script_name 的过期PID文件" rm -f "$pid_file" fi fi # Check lock file if [[ -f "$lock_file" ]]; then local lock_pid=$(cat "$lock_file") if kill -0 "$lock_pid" 2>/dev/null; then return 0 # Running else log "WARN" "删除脚本 $script_name 的过期锁定文件" rm -f "$lock_file" fi fi return 1 # Not running } # Health check function for production monitoring health_check() { local script_name="$1" local script_file="${SCRIPT_DIR}/$(get_script_file "$script_name")" log "INFO" "执行 $script_name 的健康检查..." # Check if script file exists and is executable if [[ ! -f "$script_file" ]]; then log "ERROR" "$script_name 脚本文件不存在: $script_file" return 1 fi if [[ ! -x "$script_file" ]]; then log "WARN" "$script_name 脚本文件没有执行权限,正在修复..." chmod +x "$script_file" fi # Check database connectivity if ! mysql -h"$DB_HOST" -P"$DB_PORT" -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" -e "SELECT 1;" >/dev/null 2>&1; then log "ERROR" "$script_name 数据库连接失败" return 1 fi # Check if script is running if ! is_running "$script_name"; then log "WARN" "$script_name 脚本未运行" return 1 fi log "INFO" "$script_name 健康检查通过" return 0 } # Get system resource usage get_system_info() { log "INFO" "=== 系统资源信息 ===" # CPU usage local cpu_usage=$(top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{print 100 - $1}') log "INFO" "CPU使用率: ${cpu_usage}%" # Memory usage local mem_info=$(free -h | grep "^Mem:") log "INFO" "内存信息: $mem_info" # Disk usage local disk_info=$(df -h "$SCRIPT_DIR" | tail -1 | awk '{print $1 ": " $5 " used (" $4 " free)"}') log "INFO" "磁盘使用: $disk_info" # Process count local process_count=$(pgrep -f "process_student.*\.sh" | wc -l) log "INFO" "运行中的学生处理进程数: $process_count" } start_script() { local script_name="$1" local script_file="${SCRIPT_DIR}/$(get_script_file "$script_name")" local pid_file=$(get_pid_file "$script_name") if [[ ! -f "$script_file" ]]; then log "ERROR" "脚本文件未找到: $script_file" return 1 fi if is_running "$script_name"; then log "INFO" "$script_name 已在运行" return 0 fi log "INFO" "正在启动 $script_name..." chmod +x "$script_file" # Start script in background nohup "$script_file" > /dev/null 2>&1 & local pid=$! # Wait a moment for script to start sleep 2 if kill -0 "$pid" 2>/dev/null; then echo $pid > "$pid_file" log "INFO" "$script_name 启动成功 (PID: $pid)" return 0 else log "ERROR" "$script_name 启动失败" return 1 fi } stop_script() { local script_name="$1" local pid_file=$(get_pid_file "$script_name") local lock_file=$(get_lock_file "$script_name") local stopped=false log "INFO" "正在停止 $script_name..." # 尝试通过PID文件停止 if [[ -f "$pid_file" ]]; then local pid=$(cat "$pid_file") if kill -0 "$pid" 2>/dev/null; then kill "$pid" sleep 2 if ! kill -0 "$pid" 2>/dev/null; then log "INFO" "$script_name 停止成功 (PID: $pid)" stopped=true fi fi rm -f "$pid_file" fi # 尝试通过锁定文件停止 if [[ -f "$lock_file" ]]; then local lock_pid=$(cat "$lock_file") if kill -0 "$lock_pid" 2>/dev/null; then kill "$lock_pid" sleep 2 if ! kill -0 "$lock_pid" 2>/dev/null; then log "INFO" "$script_name 停止成功 (PID: $lock_pid)" stopped=true fi fi rm -f "$lock_file" fi if [[ "$stopped" == false ]]; then log "WARN" "$script_name 可能未运行或优雅停止失败" fi } restart_script() { local script_name="$1" log "INFO" "正在重启 $script_name..." stop_script "$script_name" sleep 3 start_script "$script_name" } status_script() { local script_name="$1" if is_running "$script_name"; then local pid_file=$(get_pid_file "$script_name") local lock_file=$(get_lock_file "$script_name") if [[ -f "$pid_file" ]]; then local pid=$(cat "$pid_file") log "INFO" "$script_name 正在运行 (PID: $pid)" elif [[ -f "$lock_file" ]]; then local lock_pid=$(cat "$lock_file") log "INFO" "$script_name 正在运行 (PID: $lock_pid)" else log "INFO" "$script_name 状态未知" fi else log "INFO" "$script_name 未运行" fi } show_usage() { cat << EOF 用法: $0 <命令> <脚本名称> 命令: start 启动指定的脚本 stop 停止指定的脚本 restart 重启指定的脚本 status 显示指定脚本的状态 health 执行脚本健康检查 startall 启动所有脚本 stopall 停止所有脚本 statusall 显示所有脚本的状态 info 显示系统资源信息 脚本名称: changes 学生状态变更脚本 (休学/退学/毕业) - Ubuntu兼容修复版本 resume 学生复学脚本 (复学) - Ubuntu兼容修复版本 all 所有脚本 生产环境示例: $0 startall # 启动所有学生处理脚本 $0 statusall # 检查所有脚本运行状态 $0 health all # 执行所有脚本的健康检查 $0 info # 查看系统资源使用情况 $0 restart changes # 重启状态变更处理脚本 $0 stopall # 停止所有处理脚本 传统示例: $0 start changes # 启动状态变更处理脚本 $0 stop resume # 停止复学处理脚本 $0 status changes # 显示状态变更脚本状态 注意: 本脚本使用修复版本,已适配Ubuntu环境,解决了循环处理和数据库查询的兼容性问题 EOF } # Production environment initialization init_production_env() { log "INFO" "初始化生产环境..." # Check script files exist for script_name in "${!SCRIPTS[@]}"; do local script_file="${SCRIPT_DIR}/${SCRIPTS[$script_name]}" if [[ ! -f "$script_file" ]]; then log "ERROR" "脚本文件不存在: $script_file" log "ERROR" "请确保已部署修复版本的脚本文件" exit 1 fi done # Test database connectivity if ! mysql -h"$DB_HOST" -P"$DB_PORT" -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" -e "SELECT 1;" >/dev/null 2>&1; then log "ERROR" "数据库连接失败,请检查配置" exit 1 fi log "INFO" "生产环境初始化完成" } # Main function main() { # Initialize production environment init_production_env local command="${1:-}" local script_name="${2:-}" case "$command" in start) if [[ "$script_name" == "all" ]]; then for script in "${SCRIPT_NAMES[@]}"; do start_script "$script" done elif get_script_file "$script_name" >/dev/null 2>&1; then start_script "$script_name" else log "ERROR" "未知的脚本名称: $script_name" show_usage exit 1 fi ;; stop) if [[ "$script_name" == "all" ]]; then for script in "${SCRIPT_NAMES[@]}"; do stop_script "$script" done elif get_script_file "$script_name" >/dev/null 2>&1; then stop_script "$script_name" else log "ERROR" "未知的脚本名称: $script_name" show_usage exit 1 fi ;; restart) if [[ "$script_name" == "all" ]]; then for script in "${SCRIPT_NAMES[@]}"; do restart_script "$script" done elif get_script_file "$script_name" >/dev/null 2>&1; then restart_script "$script_name" else log "ERROR" "未知的脚本名称: $script_name" show_usage exit 1 fi ;; status) if [[ "$script_name" == "all" ]]; then for script in "${SCRIPT_NAMES[@]}"; do status_script "$script" done elif get_script_file "$script_name" >/dev/null 2>&1; then status_script "$script_name" else log "ERROR" "未知的脚本名称: $script_name" show_usage exit 1 fi ;; startall) log "INFO" "正在启动所有脚本..." for script in "${SCRIPT_NAMES[@]}"; do start_script "$script" done ;; stopall) log "INFO" "正在停止所有脚本..." for script in "${SCRIPT_NAMES[@]}"; do stop_script "$script" done ;; statusall) log "INFO" "正在检查所有脚本的状态..." for script in "${SCRIPT_NAMES[@]}"; do status_script "$script" done ;; health) if [[ "$script_name" == "all" ]]; then local all_healthy=true for script in "${SCRIPT_NAMES[@]}"; do if ! health_check "$script"; then all_healthy=false fi done if [[ "$all_healthy" == true ]]; then log "INFO" "所有脚本健康检查通过" exit 0 else log "ERROR" "部分脚本健康检查失败" exit 1 fi elif get_script_file "$script_name" >/dev/null 2>&1; then if health_check "$script_name"; then log "INFO" "$script_name 健康检查通过" exit 0 else log "ERROR" "$script_name 健康检查失败" exit 1 fi else log "ERROR" "未知的脚本名称: $script_name" show_usage exit 1 fi ;; info) get_system_info ;; *) log "ERROR" "未知命令: $command" show_usage exit 1 ;; esac } # Run main function main "$@"