近期有个小伙伴邀请我去回答问题linux中如何关闭后台运行的sh脚本 回到完问题后,我觉得可以基于这个话题,写一个更完备一点的实现思路供大家参考。
使用场景说明
在服务器上管理服务时我们经常需要启动、停止、重启脚本。
启动脚本时需要设置一些运行时的环境、指定运行时的一些参数等,如果每次启动脚本都是都逐一输入会比较麻烦,那么我们就可以把这些固定的流程都写入脚本中,启动时一键命令启动即可。
停止脚本也是一样,我们一般需要ps找到运行的进程号,确认是我们的脚本后再执行kill。这些过程也都可以自动化。
脚本示例与说明
# 获取脚本所在的目录
CUR_DIR=$(dirname $0)
# 进入到该目录运行,这样可以规避绝对路径的问题,你的脚本迁移到任何目录都可以正常运行
cd ${CUR_DIR}
# 该函数running正常退出,not running则退出非0值
check_server_is_running() {
[[ ! -f "pid" ]] && touch pid
# 获取
PID=$(cat pid)
# 首先判断pid是否存在
if [ "${PID}" = "" ]
then
echo "server is not running."
return 1
fi
# 再判断指定的PID是否在当前目录运行,防止因历史异常原因遗留了失效PID,正好该PID又被其他进程占用,造成误杀。
if [[ -d /proc/${PID}/cwd ]] && ls -ahl /proc/${PID}/cwd | grep -q "${CUR_DIR}"
then
echo "server is running."
return 0
else
# 清空失效的进程号并异常退出
echo "" > pid
return 1
fi
}
start() {
# 启动前判断日志目录是否存在,不存在就创建
[[ ! -d "../logs" ]] && mkdir -p ../logs
# 启动服务
sh test.sh > ../logs/start.log 2>&1 &
# 保存PID,以供stop使用
PID=$!
echo "server start succ, PID=${PID}!"
echo "${PID}" > pid
}
stop() {
# get pid
PID=$(cat pid)
kill -9 ${PID}
}
case C"$1" in
Cstart)
check_server_is_running || start
echo "start done."
;;
Cstop)
check_server_is_running && stop
echo "stop done."
;;
Crestart)
check_server_is_running && stop || start
echo "restart done."
;;
C*)
echo "Usage: $0 {start|stop|restart}"
;;
esac
代码释义(供新手参考)
- !
$0 在shell脚本中指当前运行的脚本本身,$1就是脚本的第一个参数,依次类推,$2就是传给脚本的第二个参数。
$! 在shell中可以获取上一个命令的进程号,这个脚本中服务启动成功后的进程号就是通过这个参数获取并保存到一个文件的。 - dirname
该命令可以获取文件所在的目录,最常见的用法就是dirname $0
获取当前脚本所在的目录。并且cd到这个目录中。
脚本中我们经常使用相对路径,这样编写的脚本就不会收到程序的绝对路径影响。 - &&和||
在shell中我们经常执行一个命令时需要依赖上一个命令的执行情况。
譬如启动脚本程序,我们启动前需要判断当前程序是否在运行,在运行我们就不重新启动,没有运行我们就启动。
此处我们用的是check_server_is_running || start
cmd1||cmd2连接的两个命令的执行关系是。cmd1执行成功(程序退出码为0)就不执行cmd2,执行失败(程序退出码非0)才执行cmd2.
cmd1&&cmd2连接的两个命令的执行关系与上面刚好相反。cmd1执行成功(程序退出码为0)才执行cmd2,执行失败(程序退出码非0)就不执行cmd2. - 判断语句
本脚本里面用了2种判断语句,if判断语句和[[ 判断语句 ]]
if与很多其他语言里面的一样。[[ 判断语句 ]]
是一种快捷判断方式,它与 || 或者 && 符号组合使用就可以到达if else的效果。 - proc文件系统
linux系统中一切都是文件,/proc目录下存储的就是系统正在运行的程序的各种资源使用情况。
proc文件系统是linux的一种虚拟文件系统,只存储在内存中,只有正在运行中的程序采用。/proc/${PID}
目录下可以看到进程{PID}/cwd`是进程${PID}所在的目录,上面的程序就是通过这个目录来增强校验,防止异常情况(譬如手动kill或者程序异常退出,pid文件的PID没有被清除,正好这个PID被重新分配给了其他进程)时,造成误杀。 - grep -q
grep -q
通常与if语句一起使用,grep -q
匹配成功就是true,匹配失败就是false - shell脚本的 switch语句
case esac
就等同于其他语言的switch语句。
脚本在所有的字符判断前面加了一个C就是为了防止输入的参数为空,让系统误以为缺失参数,可能带来语法错误的问题。
我们在写shell脚本时为了兼容空字符串可能带来语法错误的问题,经常会给参与比较的对象统一加一个前缀字符。