项目的一个需求:为防止脚本重复调度,导致同时运行时相互冲突,需要在脚本运行开始前创建一个文件,结束时删除。
脚本启动时判断一下文件是否存在,如果存在则退出。
最开始这样做没发现问题,但跑一段时间后,发现如果进程中间退出没删除文件就会出现问题。
而且有时希望等待一段时间后不管有没有已启动同名脚本,都往下走。
基于以上考虑,最近将这个防止重复启动的逻辑抽离出来成为独立脚本,并增加了一些控制逻辑,这样以后需要类似功能直接调用这个脚本就好。
流程图:
代码:
#! /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}