背景
有些时候我们写的shell脚本中有一些后台任务,当脚本的流程已经执行到结尾处或将其kill掉时,这些后台任务会直接挂靠在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进程是在循环中(for、while、until均可),那就麻烦了。
例如下面的例子,直接将循环放入后台,杀掉sleep、或者exit、或者杀掉脚本自身进程、或者让脚本自动退出、甚至exec退出当前脚本shell都是无效的。
[root@mariadb ~]# cat test1.sh
#!/bin/bash
echo $BASHPID
while true;do
sleep 50
echo 1
done &
killall sleep
kill $BASHPID
为了分析,新建一个脚本test2.sh:
#!/bin/bash
echo $BASHPID
while true;do
sleep 50
echo 1
done &
sleep 60
然后在脚本执行的60秒内查看test2.sh进程的信息:
[root@mariadb ~]# pstree -p | grep 'test2.sh'
| `-bash(2687)---test2.sh(2923)-+-sleep(2925)
| `-test2.sh(2924)---sleep(2926)
其中pid=2923的test2.sh进程是脚本自身进程,pid=2924的test2.sh进程是while开始运行后为while提供执行环境的子shell进程(为什么会生成这个进程,见我的另一篇文章)。
所以,对于前面的test1.sh进程,杀掉了 $BAS