如何让shell脚本自杀
有些时候我们写的shell脚本中有一些后台任务,当脚本的流程已经执行到结尾处并退出时,这些后台任务会直接挂靠在init/systemd进程下,而不会随着脚本退出而停止。
例如:
[root@mariadb ~]# cat test1.sh #!/bin/bash echo $BASHPID sleep 50 & [root@mariadb ~]# ps -elf | grep slee[p] 0 S root 10806 1 0 80 0 - 26973 hrtime 19:26 pts/1 00:00:00 sleep 50
从结果中可以看到,脚本退出后,sleep进程的父进程变为了1,也就是挂在了init/systemd进程下。
这时我们可以在脚本中直接使用kill命令杀掉sleep进程。
[root@mariadb ~]# cat test1.sh #!/bin/bash echo $BASHPID sleep 50 & kill $!
但是,如果这个sleep进程是在循环中,那就麻烦了。
例如下面的例子,杀掉sleep、或者杀掉脚本自身进程、或者让脚本自动退出、甚至exec退出当前脚本shell都是无效的。
[root@mariadb ~]# cat test1.sh #!/bin/bash echo $BASHPID while true;do sleep 50 echo 1 done & killall sleep kill $BASHPID
因为sleep在while中循环,杀了sleep后,稍后又会生成一个sleep。而杀掉脚本自身、或者让脚本自动退出、或者使用exec,由于有循环存在,它都会重新fork一个脚本进程出来。注意,这时循环的进程不会挂在init/systemd进程下,而是挂在一个新的脚本进程之下。
[root@mariadb ~]# ./test1.sh 10859 ./test1.sh: line 7: 10862 Terminated sleep 50 Terminated 1 [root@mariadb ~]# pstree -p | grep sleep |-test1.sh(10860)---sleep(10863)
从结果中可以看到test1.sh的10859进程被自身杀掉了,但是重新生成了一个10860的test1.sh进程,且不断生成的sleep进程也总是在test1.sh下。
除非我们手动杀掉新生成的test1.sh,否则这个脚本将无限循环下去。但是,这不是很麻烦吗?
那么如何实现"脚本自杀"?其实很简单,只要在脚本退出前,使用killall命令杀掉脚本进程即可。
[root@mariadb ~]# cat test1.sh #!/bin/bash echo $BASHPID while true;do sleep 50 echo 1 done & killall `basename $0`
这样,在脚本退出前,内核准备fork新的test1.sh进程接管后台的while内的sleep进程时,killall会将这个新的test1.sh也杀掉,这样后台进程就随着test1.sh也一起消逝了。
这里的关键点是,当脚本中有循环的后台任务时,脚本退出有一个过程:(1)脚本准备退出-->(2)内核fork新的脚本进程用来接管脚本内的后台循环任务-->(3)新脚本进程接管后台任务-->(4)旧脚本进程消逝。而killall正好赶在第(3)步之前将新旧脚本进程都杀掉,这使得脚本进程成功自杀。
回到Linux系列文章大纲:http://www.cnblogs.com/f-ck-need-u/p/7048359.html
回到网站架构系列文章大纲:http://www.cnblogs.com/f-ck-need-u/p/7576137.html
回到数据库系列文章大纲:http://www.cnblogs.com/f-ck-need-u/p/7586194.html
转载请注明出处:http://www.cnblogs.com/f-ck-need-u/p/8661501.html
注:若您觉得这篇文章还不错请点击右下角推荐,您的支持能激发作者更大的写作热情,非常感谢!
下一篇: 服务器端常见的Linux操作