Bash 陷阱未杀死 children,导致意外 ctrl-c 行为
Bash trap not killing children, causes unexpected ctrl-c behavior
编辑
对于未来的读者。这个问题的根源实际上归结为 运行 在交互式 shell 中使用函数而不是将其放在单独的脚本中。
另外,我最初发布的代码中有很多地方可以改进。请查看 could/should 做得更好的评论。
/编辑
我有一个 bash 函数,用于在目录中的文件更改时在后台重新 运行 进程(类似于 G运行t,但用于一般用途)。在 运行ning:
期间脚本按预期运行
- 子进程正确启动(包括任何children)
- 文件更改时,子程序被杀死(包括children)并重新启动
但是,在退出时 (ctrl-c) none 个进程被终止。此外,再次按 ctrl-c 将终止当前终端会话。我假设这是我的陷阱的问题,但一直无法确定问题的原因。
这里是rerun.sh
的代码
#!/bin/bash
# rerun.sh
_kill_children() {
isTop=
curPid=
# Get pids of children
children=`ps -o pid --no-headers --ppid ${curPid}`
for child in $children
do
# Call this function to get grandchildren as well
_kill_children 0 $child
done
# Parent calls this with 1, all other with 0 so only children are killed
if [[ $isTop -eq 0 ]]; then
kill -9 $curPid 2> /dev/null
fi
}
rerun() {
trap " _kill_children 1 $$; exit 0" SIGINT SIGTERM
FORMAT=$(echo -e "3[1;33m%w%f3[0m written")
#Command that should be repeatedly run is passed as args
args=$@
$args &
#When a file changes in the directory, rerun the process
while inotifywait -qre close_write --format "$FORMAT" .
do
#Kill current bg proc and it's children
_kill_children 1 $$
$args & #Rerun the proc
done
}
#This is sourced in my bash profile so I can run it any time
要对此进行测试,请创建一对可执行文件 parent.sh 和 child.sh,如下所示:
#!/bin/bash
#parent.sh
./child.sh
#!/bin/bash
#child.sh
sleep 86400
然后获取 rerun.sh 文件和 运行 rerun ./parent.sh
。在另一个终端 window 我 watch "ps -ef | grep pts/4"
查看 re运行 的所有进程(在本例中为 pts/4)。触摸目录中的文件会触发 parent.sh 和 children 的重新启动。 [ctrl-c] 退出,但留下 pids 运行ning。 [ctrl-c] 再次杀死 bash 和 pts/4 上的所有其他进程。
期望的行为:在 [ctrl-c] 上,杀死 children 并正常退出到 shell。帮忙?
--
代码来源:
Inotify 想法来自:https://exyr.org/2011/inotify-run/
杀死 children 来自:http://riccomini.name/posts/linux/2012-09-25-kill-subprocesses-linux-bash/
首先,这不是一个值得遵循的好习惯。明确跟踪您的 children:
children=( )
foo & children+=( "$!" )
...然后,您可以明确地杀死或等待它们,请参阅 "${children[@]}"
以获取列表。如果你也想获得 grandchildren,这是 fuser -k
和锁文件的好用户:
lockfile_name="$(mktemp /tmp/lockfile.XXXXXX)" # change appropriately
trap 'rm -f "$lockfile_name"' 0
exec 3>"$lockfile_name" # open lockfile on FD 3
kill_children() {
# close our own handle on the lockfile
exec 3>&-
# kill everything that still has it open (our children and their children)
fuser -k "$lockfile_name" >/dev/null
# ...then open it again.
exec 3>"$lockfile_name"
}
rerun() {
trap 'kill_children; exit 0' SIGINT SIGTERM
printf -v format '%b' "3[1;33m%w%f3[0m written"
"$@" &
#When a file changes in the directory, rerun the process
while inotifywait -qre close_write --format "$format" .; do
kill_children
"$@" &
done
}
编辑
对于未来的读者。这个问题的根源实际上归结为 运行 在交互式 shell 中使用函数而不是将其放在单独的脚本中。
另外,我最初发布的代码中有很多地方可以改进。请查看 could/should 做得更好的评论。
/编辑
我有一个 bash 函数,用于在目录中的文件更改时在后台重新 运行 进程(类似于 G运行t,但用于一般用途)。在 运行ning:
期间脚本按预期运行- 子进程正确启动(包括任何children)
- 文件更改时,子程序被杀死(包括children)并重新启动
但是,在退出时 (ctrl-c) none 个进程被终止。此外,再次按 ctrl-c 将终止当前终端会话。我假设这是我的陷阱的问题,但一直无法确定问题的原因。
这里是rerun.sh
的代码#!/bin/bash
# rerun.sh
_kill_children() {
isTop=
curPid=
# Get pids of children
children=`ps -o pid --no-headers --ppid ${curPid}`
for child in $children
do
# Call this function to get grandchildren as well
_kill_children 0 $child
done
# Parent calls this with 1, all other with 0 so only children are killed
if [[ $isTop -eq 0 ]]; then
kill -9 $curPid 2> /dev/null
fi
}
rerun() {
trap " _kill_children 1 $$; exit 0" SIGINT SIGTERM
FORMAT=$(echo -e "3[1;33m%w%f3[0m written")
#Command that should be repeatedly run is passed as args
args=$@
$args &
#When a file changes in the directory, rerun the process
while inotifywait -qre close_write --format "$FORMAT" .
do
#Kill current bg proc and it's children
_kill_children 1 $$
$args & #Rerun the proc
done
}
#This is sourced in my bash profile so I can run it any time
要对此进行测试,请创建一对可执行文件 parent.sh 和 child.sh,如下所示:
#!/bin/bash
#parent.sh
./child.sh
#!/bin/bash
#child.sh
sleep 86400
然后获取 rerun.sh 文件和 运行 rerun ./parent.sh
。在另一个终端 window 我 watch "ps -ef | grep pts/4"
查看 re运行 的所有进程(在本例中为 pts/4)。触摸目录中的文件会触发 parent.sh 和 children 的重新启动。 [ctrl-c] 退出,但留下 pids 运行ning。 [ctrl-c] 再次杀死 bash 和 pts/4 上的所有其他进程。
期望的行为:在 [ctrl-c] 上,杀死 children 并正常退出到 shell。帮忙?
-- 代码来源:
Inotify 想法来自:https://exyr.org/2011/inotify-run/
杀死 children 来自:http://riccomini.name/posts/linux/2012-09-25-kill-subprocesses-linux-bash/
首先,这不是一个值得遵循的好习惯。明确跟踪您的 children:
children=( )
foo & children+=( "$!" )
...然后,您可以明确地杀死或等待它们,请参阅 "${children[@]}"
以获取列表。如果你也想获得 grandchildren,这是 fuser -k
和锁文件的好用户:
lockfile_name="$(mktemp /tmp/lockfile.XXXXXX)" # change appropriately
trap 'rm -f "$lockfile_name"' 0
exec 3>"$lockfile_name" # open lockfile on FD 3
kill_children() {
# close our own handle on the lockfile
exec 3>&-
# kill everything that still has it open (our children and their children)
fuser -k "$lockfile_name" >/dev/null
# ...then open it again.
exec 3>"$lockfile_name"
}
rerun() {
trap 'kill_children; exit 0' SIGINT SIGTERM
printf -v format '%b' "3[1;33m%w%f3[0m written"
"$@" &
#When a file changes in the directory, rerun the process
while inotifywait -qre close_write --format "$format" .; do
kill_children
"$@" &
done
}