用shell腳本自己一個(gè)秒級(jí)定時(shí)任務(wù)
設(shè)計(jì)一個(gè)定時(shí)任務(wù)管理工具,使用 Shell 腳本實(shí)現(xiàn),核心目標(biāo)是實(shí)現(xiàn)每秒執(zhí)行一次,并保證任務(wù)異步、非阻塞執(zhí)行。以下是實(shí)現(xiàn)的詳細(xì)設(shè)計(jì):
工具功能
任務(wù)注冊(cè):支持注冊(cè)新任務(wù)。
任務(wù)刪除:支持刪除已有任務(wù)。
任務(wù)執(zhí)行:每秒執(zhí)行一次任務(wù)。
異步非阻塞:任務(wù)在獨(dú)立的進(jìn)程中執(zhí)行,不影響主進(jìn)程。
日志記錄:任務(wù)執(zhí)行結(jié)果記錄到日志文件中。
腳本設(shè)計(jì)
#!/bin/bash # 配置文件和日志文件路徑 CONFIG_FILE="./tasks.conf" LOG_FILE="./tasks.log" PID_FILE="./scheduler.pid" RELOAD_SIGNAL_FILE="./reload.signal" # 初始化配置文件和信號(hào)文件 if [ ! -f "$CONFIG_FILE" ]; then touch "$CONFIG_FILE" fi if [ -f "$RELOAD_SIGNAL_FILE" ]; then rm -f "$RELOAD_SIGNAL_FILE" fi # 任務(wù)狀態(tài)跟蹤表 declare -A LAST_EXECUTION_TIMES # 定時(shí)調(diào)度器 scheduler() { echo $$ > "$PID_FILE" echo "調(diào)度器啟動(dòng),PID: $$" while true; do # 檢查是否需要重新加載配置 if [ -f "$RELOAD_SIGNAL_FILE" ]; then echo "$(date '+%Y-%m-%d %H:%M:%S') [INFO] 重新加載配置" >> "$LOG_FILE" LAST_EXECUTION_TIMES=() # 清空上次執(zhí)行記錄 rm -f "$RELOAD_SIGNAL_FILE" fi # 當(dāng)前時(shí)間戳 local current_time=$(date +%s) # 動(dòng)態(tài)加載配置文件并執(zhí)行任務(wù) while IFS="=" read -r task_name interval command; do [ -z "$task_name" ] || [ -z "$interval" ] || [ -z "$command" ] && continue # 獲取上次執(zhí)行時(shí)間,如果沒(méi)有記錄,默認(rèn)為0 local last_exec_time=${LAST_EXECUTION_TIMES[$task_name]:-0} # 判斷是否到達(dá)執(zhí)行間隔 if (( current_time - last_exec_time >= interval )); then ( #安裝任務(wù)名記錄任務(wù)執(zhí)行情況 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')執(zhí)行start" >> "$TASK_LOG_FILE" eval "$command" >> "$TASK_LOG_FILE" 2>&1 echo "end" >> "$TASK_LOG_FILE" ) & LAST_EXECUTION_TIMES[$task_name]=$current_time # 更新任務(wù)的最后執(zhí)行時(shí)間 fi done < "$CONFIG_FILE" # 等待 1 秒 sleep 1 done } # 停止調(diào)度器 stop_scheduler() { if [ -f "$PID_FILE" ]; then local pid pid=$(cat "$PID_FILE") kill "$pid" && rm -f "$PID_FILE" echo "調(diào)度器已停止" else echo "調(diào)度器未運(yùn)行" fi } # 添加任務(wù) add_task() { local task_name="$1" local interval="$2" local command="$3" if grep -q "^$task_name=" "$CONFIG_FILE"; then echo "任務(wù) '$task_name' 已存在" return 1 fi echo "$task_name=$interval=$command" >> "$CONFIG_FILE" echo "任務(wù) '$task_name' 添加成功" } # 刪除任務(wù) remove_task() { local task_name="$1" if ! grep -q "^$task_name=" "$CONFIG_FILE"; then echo "任務(wù) '$task_name' 不存在" return 1 fi sed -i "/^$task_name=/d" "$CONFIG_FILE" echo "任務(wù) '$task_name' 已刪除" } # 重新加載配置 reload_scheduler() { if [ ! -f "$PID_FILE" ]; then echo "調(diào)度器未運(yùn)行,無(wú)法重新加載配置" return 1 fi touch "$RELOAD_SIGNAL_FILE" echo "配置重新加載信號(hào)已發(fā)送" } # 顯示幫助 show_help() { echo "用法: $0 [命令] [參數(shù)]" echo "命令:" echo " start 啟動(dòng)調(diào)度器" echo " stop 停止調(diào)度器" echo " reload 重新加載配置文件" echo " add 任務(wù)名 間隔秒數(shù) 命令 添加任務(wù)到配置文件" echo " remove 任務(wù)名 從配置文件中刪除任務(wù)" 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 文件存儲(chǔ)任務(wù)配置,每行一個(gè)任務(wù),格式為:
任務(wù)名=間隔秒數(shù)=命令
例如:
task1=5=echo '每5秒運(yùn)行一次' task2=30=date task3=10=sleep 2 && echo '任務(wù)完成'
啟動(dòng)調(diào)度器
./scheduler.sh start
添加任務(wù)
./scheduler.sh add "task1" 5 "echo 'Hello, every 5 seconds!'"
刪除任務(wù)
./scheduler.sh remove "task1"
重新加載配置
./scheduler.sh reload
停止調(diào)度器
./scheduler.sh stop
同時(shí)支持手動(dòng)修改配置文件修改任務(wù)
實(shí)現(xiàn)原理
任務(wù)間隔控制:使用關(guān)聯(lián)數(shù)組 LAST_EXECUTION_TIMES 記錄每個(gè)任務(wù)的最后執(zhí)行時(shí)間。
動(dòng)態(tài)間隔判斷:在每秒循環(huán)中,檢查當(dāng)前時(shí)間與任務(wù)的最后執(zhí)行時(shí)間的差值是否達(dá)到設(shè)定的間隔秒數(shù)。
動(dòng)態(tài)配置管理:配置文件支持實(shí)時(shí)修改,結(jié)合 reload 功能可立即生效。
異步執(zhí)行:每個(gè)任務(wù)獨(dú)立進(jìn)程執(zhí)行,不阻塞其他任務(wù)。
優(yōu)點(diǎn)
支持自定義間隔:滿足不同任務(wù)執(zhí)行頻率的需求。
動(dòng)態(tài)加載:無(wú)需重啟即可更新任務(wù)配置。
高可擴(kuò)展性:可根據(jù)需要進(jìn)一步優(yōu)化,如添加任務(wù)優(yōu)先級(jí)或日志清理功能。
為定時(shí)器創(chuàng)建 systemd 服務(wù)
創(chuàng)建一個(gè)新的 systemd 服務(wù)文件:你可以將服務(wù)單元文件放在 /etc/systemd/system/ 目錄下。假設(shè)我們創(chuàng)建一個(gè)名為 task-scheduler.service 的服務(wù)。
sudo nano /etc/systemd/system/task-scheduler.service
編輯服務(wù)單元文件:以下是 task-scheduler.service 的內(nèi)容模板。你可以根據(jù)需要修改。
[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 識(shí)別新的服務(wù)單元文件。
sudo systemctl daemon-reload
啟用服務(wù):設(shè)置服務(wù)在系統(tǒng)啟動(dòng)時(shí)自動(dòng)啟動(dòng)。
sudo systemctl enable task-scheduler.service
開機(jī)啟動(dòng)服務(wù):立即啟動(dòng)任務(wù)調(diào)度器服務(wù)。
sudo systemctl start task-scheduler.service
檢查服務(wù)狀態(tài):確認(rèn)服務(wù)是否正常啟動(dòng)。
sudo systemctl status task-scheduler.service
停止服務(wù):
sudo systemctl stop task-scheduler.service
重新加載配置(如果你修改了配置文件或腳本):
sudo systemctl reload task-scheduler.service
可以通過(guò) journalctl 命令查看服務(wù)日志,以便調(diào)試:
sudo journalctl -u task-scheduler.service
到此這篇關(guān)于用shell腳本自己一個(gè)秒級(jí)定時(shí)任務(wù)的文章就介紹到這了,更多相關(guān)shell定時(shí)任務(wù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
linux獲取系統(tǒng)啟動(dòng)時(shí)間示例詳解
這篇文章主要介紹了linux獲取系統(tǒng)啟動(dòng)時(shí)間的示例,需要的朋友可以參考下2014-02-02shell linux中如何用shell寫一個(gè)占用CPU的腳本
本文主要介紹了shell linux中如何用shell寫一個(gè)占用CPU的腳本,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09Linux?shell字符串截取、替換、刪除以及trim代碼示例
在Shell編程中,截取、替換、刪除字符串是非常常見的操作,這篇文章主要給大家介紹了關(guān)于Linux?shell字符串截取、替換、刪除以及trim的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-03-03Shell 實(shí)現(xiàn)多任務(wù)并發(fā)的示例代碼
本文主要介紹了Shell 實(shí)現(xiàn)多任務(wù)并發(fā)的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06linux網(wǎng)絡(luò)編程用到的網(wǎng)絡(luò)函數(shù)詳解用和使用示例
本文對(duì)linux網(wǎng)絡(luò)編程用到的網(wǎng)絡(luò)函數(shù)做了簡(jiǎn)單介紹,提供了使用示例供大家參考2013-11-11