項目的一個需求:為防止腳本重復調度,導致同時運行時相互沖突,需要在腳本運行開始前創建一個文件,結束時刪除。
腳本啟動時判斷一下文件是否存在,如果存在則退出。
最開始這樣做沒發現問題,但跑一段時間后,發現如果進程中間退出沒刪除文件就會出現問題。
而且有時希望等待一段時間后不管有沒有已啟動同名腳本,都往下走。
基於以上考慮,最近將這個防止重復啟動的邏輯抽離出來成為獨立腳本,並增加了一些控制邏輯,這樣以后需要類似功能直接調用這個腳本就好。
流程圖:
代碼:
#! /bin/sh # singleton.sh 進程名($0) 進程id($$) 工作目錄 休眠時間 嘗試次數 # 嘗試次數為0代表無限次 # 本腳本的作用是防止進程重復啟動(類似單例) 腳本啟動后會在工作目錄生成一個進程信息文件(pidfile)起到唯一鎖作用 # 如果pidfile里面的進程不存在 腳本就會結束 否則會一直等待直到嘗試次數超限 if [ $# != 5 ] then echo "usage:singleton.sh 進程名(\$0) 進程id(\$\$) 工作目錄 休眠時間 嘗試次數(0代表無限次)" exit 1 fi v_proc_name=${1##*/} v_pid=$2 v_work_dir=$3 v_sleep_seconds=$4 v_retry_times=$5 # pid文件路徑 模擬文件鎖用的 v_pid_file=${v_work_dir}/singleton_run_pid echo "cmd = $0 $*" # 判斷pid文件是否存在 # 如存在判斷文件里面的進程是否存在 且進程名字相同 # 如果符合以上條件則休眠后再次嘗試 直至滿足最大嘗試次數 if [ -f ${v_pid_file} ] then echo "file[${v_pid_file}] exists!" v_times=0 v_max_retry_times=${v_retry_times} while [ ${v_times} -lt ${v_max_retry_times} -o ${v_retry_times} -eq 0 ] do v_pid_old="";v_proc_name_old=""; read v_pid_old v_proc_name_old < ${v_pid_file} if [ "${v_pid_old}" = "" -o "${v_proc_name_old}" = "" ] then echo "error pidfile [$(cat ${v_pid_file})]" break; fi # 考慮到腳本有時被強制殺掉會導致pid文件殘留沒清理,這里加上對pid文件內容判斷 if [ ${v_proc_name_old} != ${v_proc_name} ] then echo "proc_name not euqal; [${v_proc_name_old}] != [${v_proc_name}]! do next;" break; fi if [ ${v_pid} = ${v_pid_old} ] then echo "pid euqal; [${v_pid}] = [${v_pid_old}]! do next;" break; fi # grep 參數 -- 代表參數結果 后面跟的是關鍵字或文件名 這是為了避免進程名是-bash的問題 v_pid_inf=$(ps -ef|awk -v v_pid_old=${v_pid_old} '{ if($2 == v_pid_old) print $0; }'|grep -w -- ${v_proc_name}) if [ "${v_pid_inf}" = "" ] then echo "cannot find the pid[ps -ef|awk -v v_pid_old=${v_pid_old} '{ if(\$2 == v_pid_old) print \$0; }'|grep -w -- ${v_proc_name}]! do next;" break; fi v_times=$(expr ${v_times} + 1) echo "try times ${v_times}; sleep ${v_sleep_seconds}" sleep ${v_sleep_seconds} done echo "had tried ${v_times} times! do next;" fi echo "create file[${v_pid_file}]!" echo ${v_pid} ${v_proc_name} > ${v_pid_file}