退出陷阱(杀死后台循环)也不能杀死该循环运行的进程

时间:2019-11-25 15:30:05

标签: bash

我正在一个环境中运行bash脚本,该环境会在10分钟内没有输出的情况下杀死进程。因此,我正在运行一个后台进程,注入换行符以防止发生超时。

到目前为止,该方法仍然有效,但是在脚本完成后,似乎无法正确杀死keep-alive循环。睡眠命令最多持续590秒。

#!/usr/bin/env bash
# Exit on any error
set -e 

# Some other commands
# ...

(while true; do echo "" && sleep 590; done) &
trap "kill $!" EXIT
ps all
looong_running_command_with_no_output
# Exit normally

我的脚本中ps的输出:

      PID    PPID    PGID     WINPID   TTY         UID    STIME COMMAND
     1607    1606    1467       5032  ?         197608 20:21:05 /usr/bin/bash
     1468    1467    1467       4592  ?         197608 20:20:49 /usr/bin/bash
     1608    1607    1467       4064  ?         197608 20:21:05 /usr/bin/sleep
     1467       1    1467       4472  ?         197608 20:20:49 /usr/bin/bash
     1609    1606    1467       4768  ?         197608 20:21:05 /usr/bin/ps
     1606    1468    1467       4168  ?         197608 20:21:05 /usr/bin/bash

脚本完成后ps的输出:

      PID    PPID    PGID     WINPID   TTY         UID    STIME COMMAND
     1468    1467    1467       4592  ?         197608 20:20:49 /usr/bin/bash
     1637    1468    1467       2436  ?         197608 20:21:09 /usr/bin/ps
     1608       1    1467       4064  ?         197608 20:21:05 /usr/bin/sleep
     1467       1    1467       4472  ?         197608 20:20:49 /usr/bin/bash

您可以看到睡眠仍在后台运行。 PPID现在已经更改为1,因为它会将后台进程重新初始化为init。但是,我根本不明白为什么会这样。 kill是否不会终止所有子进程(在这种情况下为睡眠和回声)?

更新:我的脚本正在运行-e标志。也许这很重要。我还添加了ps的输出。

Update2: 看起来kill确实并没有杀死注释中提到的整个进程树。 pkill可以,但是我的脚本在pkill不可用的Git Bash中运行。在阅读了很多带有复杂建议的帖子后,我最终遇到了以下陷阱:

trap "kill $!; wait $! 2>/dev/null; killall sleep" EXIT

这对于我的用例来说已经足够好了。

1 个答案:

答案 0 :(得分:2)

不是那么简单...

问题是:使用( ... )语法时,您将创建一个子shell ...让我们来展示:

在某些终端控制台中:

tty
/dev/pts/24
ps --tty pts/24 fw
  PID TTY      STAT   TIME COMMAND
28983 pts/24   Ss     0:00 bash
29322 pts/24   R+     0:00  \_ ps --tty pts/24 fw

( echo foo; sleep 300 ) &
[1] 30214
foo

ps --tty pts/24 fw
  PID TTY      STAT   TIME COMMAND
28983 pts/24   Ss     0:00 bash
30214 pts/24   S      0:00  \_ bash
30215 pts/24   S      0:00  |   \_ sleep 300
30377 pts/24   R+     0:00  \_ ps --tty pts/24 fw

ps $!
  PID TTY      STAT   TIME COMMAND
30214 pts/24   S      0:00 bash

所以$!bash subshel​​l ,而sleep有另一个pid。

因此杀死$!只会杀死bash:

kill $!
[1]+  Terminated         ( echo foo; sleep 300 )

但是

ps --tty pts/24 fw
      PID TTY      STAT   TIME COMMAND
28983 pts/24   Ss     0:00 bash
30793 pts/24   R+     0:00  \_ ps --tty pts/24 fw
30215 pts/24   S      0:00 sleep 300

睡眠仍然存在。

解决方案1:

在上一个长时间运行的命令上使用exec

( echo foo; exec sleep 300 ) &
[1] 31557
foo

ps --tty pts/24 fw
      PID TTY      STAT   TIME COMMAND
28983 pts/24   Ss     0:00 bash
31557 pts/24   S      0:00  \_ sleep 300
31558 pts/24   R+     0:00  \_ ps --tty pts/24 fw

然后

kill $!
[1]+  Terminated              ( echo foo; exec sleep 300 )

ps --tty pts/24 fw
  PID TTY      STAT   TIME COMMAND
28983 pts/24   Ss     0:00 bash
31706 pts/24   R+     0:00  \_ ps --tty pts/24 fw

似乎还可以。

对于 loop ,事情变得更多了,下摆...

ps --tty pts/24 fw
  PID TTY      STAT   TIME COMMAND
28983 pts/24   Ss     0:00 bash
 1176 pts/24   R+     0:00  \_ ps --tty pts/24 fw


for ((i=3;i--;)){ sleep 300 & trap "kill $!;exit" 0 1 2 3 6 9 15;wait $!;} &
[1] 1178

ps --tty pts/24 fw
      PID TTY      STAT   TIME COMMAND
28983 pts/24   Ss     0:00 bash
 1178 pts/24   S      0:00  \_ bash
 1179 pts/24   S      0:00  |   \_ sleep 300
 1180 pts/24   R+     0:00  \_ ps --tty pts/24 fw

好吧,现在:

kill -1 $!
[1]+  Done                    for ((i=3; i--; 1))
do
    sleep 300 & trap "kill $!;exit" 0 1 2 3 6 9 15; wait $!;
done

ps --tty pts/24 fw
      PID TTY      STAT   TIME COMMAND
28983 pts/24   Ss     0:00 bash
 1299 pts/24   R+     0:00  \_ ps --tty pts/24 fw

似乎很好!