linux shell

关注公众号 jb51net

关闭
首页 > 脚本专栏 > linux shell > shell定时任务

用shell脚本自己一个秒级定时任务

作者:半桶水专家

这篇文章主要为大家详细介绍了如何使用Shell脚本实现一个定时任务管理工具,可以每秒执行一次,并保证任务异步、非阻塞执行,需要的可以参考下

设计一个定时任务管理工具,使用 Shell 脚本实现,核心目标是实现每秒执行一次,并保证任务异步、非阻塞执行。以下是实现的详细设计:

工具功能

任务注册:支持注册新任务。

任务删除:支持删除已有任务。

任务执行:每秒执行一次任务。

异步非阻塞:任务在独立的进程中执行,不影响主进程。

日志记录:任务执行结果记录到日志文件中。

脚本设计

#!/bin/bash
 
# 配置文件和日志文件路径
CONFIG_FILE="./tasks.conf"
LOG_FILE="./tasks.log"
PID_FILE="./scheduler.pid"
RELOAD_SIGNAL_FILE="./reload.signal"
 
# 初始化配置文件和信号文件
if [ ! -f "$CONFIG_FILE" ]; then
    touch "$CONFIG_FILE"
fi
if [ -f "$RELOAD_SIGNAL_FILE" ]; then
    rm -f "$RELOAD_SIGNAL_FILE"
fi
 
# 任务状态跟踪表
declare -A LAST_EXECUTION_TIMES
 
# 定时调度器
scheduler() {
    echo $$ > "$PID_FILE"
    echo "调度器启动,PID: $$"
 
    while true; do
        # 检查是否需要重新加载配置
        if [ -f "$RELOAD_SIGNAL_FILE" ]; then
            echo "$(date '+%Y-%m-%d %H:%M:%S') [INFO] 重新加载配置" >> "$LOG_FILE"
            LAST_EXECUTION_TIMES=()  # 清空上次执行记录
            rm -f "$RELOAD_SIGNAL_FILE"
        fi
 
        # 当前时间戳
        local current_time=$(date +%s)
 
        # 动态加载配置文件并执行任务
        while IFS="=" read -r task_name interval command; do
            [ -z "$task_name" ] || [ -z "$interval" ] || [ -z "$command" ] && continue
 
            # 获取上次执行时间,如果没有记录,默认为0
            local last_exec_time=${LAST_EXECUTION_TIMES[$task_name]:-0}
 
            # 判断是否到达执行间隔
            if (( current_time - last_exec_time >= interval )); then
                (   #安装任务名记录任务执行情况
                    local TASK_LOG_FILE="$task_name/$(date '+%Y-%m-%d').log"
                    if [ ! -d "$(dirname "$TASK_LOG_FILE")" ]; then
                        mkdir -p "$(dirname "$TASK_LOG_FILE")"
                    fi
                    echo "$(date +'%H:%M:%S')执行start" >> "$TASK_LOG_FILE"
                    eval "$command" >> "$TASK_LOG_FILE" 2>&1
                    echo "end" >> "$TASK_LOG_FILE"
                ) &
                LAST_EXECUTION_TIMES[$task_name]=$current_time  # 更新任务的最后执行时间
            fi
        done < "$CONFIG_FILE"
 
        # 等待 1 秒
        sleep 1
    done
}
 
# 停止调度器
stop_scheduler() {
    if [ -f "$PID_FILE" ]; then
        local pid
        pid=$(cat "$PID_FILE")
        kill "$pid" && rm -f "$PID_FILE"
        echo "调度器已停止"
    else
        echo "调度器未运行"
    fi
}
 
# 添加任务
add_task() {
    local task_name="$1"
    local interval="$2"
    local command="$3"
    if grep -q "^$task_name=" "$CONFIG_FILE"; then
        echo "任务 '$task_name' 已存在"
        return 1
    fi
    echo "$task_name=$interval=$command" >> "$CONFIG_FILE"
    echo "任务 '$task_name' 添加成功"
}
 
# 删除任务
remove_task() {
    local task_name="$1"
    if ! grep -q "^$task_name=" "$CONFIG_FILE"; then
        echo "任务 '$task_name' 不存在"
        return 1
    fi
    sed -i "/^$task_name=/d" "$CONFIG_FILE"
    echo "任务 '$task_name' 已删除"
}
 
# 重新加载配置
reload_scheduler() {
    if [ ! -f "$PID_FILE" ]; then
        echo "调度器未运行,无法重新加载配置"
        return 1
    fi
    touch "$RELOAD_SIGNAL_FILE"
    echo "配置重新加载信号已发送"
}
 
# 显示帮助
show_help() {
    echo "用法: $0 [命令] [参数]"
    echo "命令:"
    echo "  start             启动调度器"
    echo "  stop              停止调度器"
    echo "  reload            重新加载配置文件"
    echo "  add 任务名 间隔秒数 命令  添加任务到配置文件"
    echo "  remove 任务名      从配置文件中删除任务"
    echo "  help              显示帮助"
}
 
# 主逻辑
case "$1" in
    start)
        scheduler
        ;;
    stop)
        stop_scheduler
        ;;
    reload)
        reload_scheduler
        ;;
    add)
        add_task "$2" "$3" "$4"
        ;;
    remove)
        remove_task "$2"
        ;;
    help|*)
        show_help
        ;;
esac

配置文件格式

使用 tasks.conf 文件存储任务配置,每行一个任务,格式为:

任务名=间隔秒数=命令

例如:

task1=5=echo '每5秒运行一次'
task2=30=date
task3=10=sleep 2 && echo '任务完成'

启动调度器

./scheduler.sh start

添加任务

./scheduler.sh add "task1" 5 "echo 'Hello, every 5 seconds!'"

删除任务

./scheduler.sh remove "task1"

重新加载配置

./scheduler.sh reload

停止调度器

./scheduler.sh stop

同时支持手动修改配置文件修改任务

实现原理

任务间隔控制:使用关联数组 LAST_EXECUTION_TIMES 记录每个任务的最后执行时间。

动态间隔判断:在每秒循环中,检查当前时间与任务的最后执行时间的差值是否达到设定的间隔秒数。

动态配置管理:配置文件支持实时修改,结合 reload 功能可立即生效。

异步执行:每个任务独立进程执行,不阻塞其他任务。

优点

支持自定义间隔:满足不同任务执行频率的需求。

动态加载:无需重启即可更新任务配置。

高可扩展性:可根据需要进一步优化,如添加任务优先级或日志清理功能。

为定时器创建 systemd 服务

创建一个新的 systemd 服务文件:你可以将服务单元文件放在 /etc/systemd/system/ 目录下。假设我们创建一个名为 task-scheduler.service 的服务。

sudo nano /etc/systemd/system/task-scheduler.service

编辑服务单元文件:以下是 task-scheduler.service 的内容模板。你可以根据需要修改。

[Unit]
Description=Task Scheduler Service
After=network.target
 
[Service]
Type=simple
User=root
ExecStart=/path/to/your/scheduler.sh start
ExecReload=/path/to/your/scheduler.sh reload
ExecStop=/path/to/your/scheduler.sh stop
WorkingDirectory=/path/to/working/directory
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
SyslogIdentifier=task-scheduler
 
[Install]
WantedBy=multi-user.target

重新加载 systemd 配置:让 systemd 识别新的服务单元文件。

sudo systemctl daemon-reload

启用服务:设置服务在系统启动时自动启动。

sudo systemctl enable task-scheduler.service

开机启动服务:立即启动任务调度器服务。

sudo systemctl start task-scheduler.service

检查服务状态:确认服务是否正常启动。

sudo systemctl status task-scheduler.service

停止服务:

sudo systemctl stop task-scheduler.service

重新加载配置(如果你修改了配置文件或脚本):

sudo systemctl reload task-scheduler.service

可以通过 journalctl 命令查看服务日志,以便调试:

sudo journalctl -u task-scheduler.service

到此这篇关于用shell脚本自己一个秒级定时任务的文章就介绍到这了,更多相关shell定时任务内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文