如何让shell脚本自杀,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

注:若您觉得这篇文章还不错请点击右下角推荐,您的支持能激发作者更大的写作热情,非常感谢!

相关内容