[英]bash child script exits along with parent script when parent invoked interactively / by terminal, but not when invoked non-interactively / by cron
This is parent.sh: 这是parent.sh:
#!/bin/bash
trap 'exit' SIGHUP SIGINT SIGQUIT SIGTERM
if ! [ -t 0 ]; then # if running non-interactively
sleep 5 & # allow a little time for child to generate some output
set -bm # to be able to trap SIGCHLD
trap 'kill -SIGINT $$' SIGCHLD # when sleep is done, interrupt self automatically - cannot issue interrupt by keystroke since running non-interactively
fi
sudo ~/child.sh
This is child.sh: 这是child.sh:
#!/bin/bash
test -f out.txt && rm out.txt
for second in {1..10}; do
echo "$second" >> out.txt
sleep 1
done
If run the parent script in a terminal like so... 如果在这样的终端中运行父脚本...
~/parent.sh
...and after about 3 seconds, issue an interrupt by keystroke. ...大约3秒钟后,通过按键发出一个中断。 When checking out.txt a few seconds later, it will look like... 几秒钟后检查out.txt时,它看起来像...
1
2
3
...thus indicating that parent and child ended upon (keystroke) interrupt. ...因此指示父母和孩子在(按键)中断后结束。 This is corroborated by checking ps -ef
in real-time and seeing that the script processes are present before the interrupt and gone after the interrupt. 通过实时检查ps -ef
并查看脚本进程在中断之前存在并在中断之后消失,可以证实这一点。
If parent script is invoked by cron like so... 如果像cron这样调用父脚本,则...
* * * * * ~/parent.sh
...the content of out.txt is always... ... out.txt的内容始终是...
1
2
3
4
5
6
7
8
9
10
...thus indicating that at least the child did not end upon (kill command) interrupt. ...因此表明至少孩子没有在(kill命令)中断后结束。 This is corroborated by checking ps -ef
in real-time and seeing that the script processes are present before the interrupt and only the parent process is gone after the interrupt, but the child process persists until it runs its course. 通过实时检查ps -ef
并查看脚本进程是否在中断之前存在,并且只有父进程在中断之后消失,而子进程一直持续到运行过程,可以证实这一点。
Attempts to solve... 尝试解决...
set -bm
(which entails PGIDs of children differing from PGID of parent - relevant up ahead). Shell选项在这里仅是一个因素,因为父级运行set -bm
非交互式调用(这意味着子级的PGID与父级的PGID不同-以后相关)。 Other than that, both scripts show only options hB enabled, whether running interactively or not. 除此之外,这两个脚本都仅显示启用了hB的选项,无论是否以交互方式运行。 Is the problem with these solutions that the parent runs as a regular user, while the child runs as root via sudo (it will ultimately be a binary, not a suid script), so the parent cannot kill it? 这些解决方案的问题是父级以常规用户身份运行,而子级通过sudo以root身份运行(最终将是二进制文件,而不是suid脚本),因此父级无法杀死它吗? If that's what "Operation not permitted" means, why is the sudo invoked process killable when sending a keystroke interrupt via terminal? 如果那是“不允许操作”的意思,那么当通过终端发送按键中断时,为什么sudo调用的进程会被杀死?
The natural course is to avoid additional code, unless necessary - ie since the scripts behave correctly when run interactively, if feasible it's much preferred to simply apply the same behavior when running non-interactively / by cron. 自然的做法是,除非有必要,否则避免使用其他代码-即由于脚本在交互运行时会正确运行,因此,如果可行的话,在以cron非交互方式运行时,简单地应用相同的行为是更为可取的。
The bottom line question is, what can be done to make an interrupt (or term) signal issued while running non-interactively, produce the same behavior as an interrupt signal issued when running interactively? 最重要的问题是,如何使非交互式运行时发出的中断(或术语)信号产生与交互式运行时发出的中断信号相同的行为?
Thanks. 谢谢。 Any help is greatly appreciated. 任何帮助是极大的赞赏。
CTRL-C
and convert it to SIGINT
and send to all processes in the foreground process group (the script itself and the sudo
command). 当您从交互式外壳程序手动运行脚本(通常在pty上运行)时, 终端驱动程序会捕获CTRL-C
并将其转换为SIGINT
并发送给前台进程组中的所有进程 (脚本本身和sudo
命令) )。 SIGINT
to the shell script itself and the sudo
command will continue running and bash will not kill its child when it exits for this kind of scenario. 当脚本从cron运行时,您仅将SIGINT
发送到shell脚本本身,并且sudo
命令将继续运行,并且bash在这种情况下退出时不会杀死其子级。 To explicitly send a signal to a whole process group you can use the negative process group ID . 要将信号显式发送到整个过程组,可以使用否定的过程组ID 。 For your case the pgid should be the PID of the shell script so try like this: 对于您的情况,pgid应该是shell脚本的PID,因此请尝试如下操作:
trap 'kill -SIGINT -$$' SIGCHLD
It turns out my assumption about the value of pgid is wrong. 事实证明,我对pgid的值的假设是错误的。 Just did a test with this simple cron.sh
: 只是用这个简单的cron.sh
做了一个测试:
#!/bin/bash
set -m
sleep 888 &
sudo sleep 999
and crontal -l
looks like this: 和crontal -l
看起来像这样:
30 * * * * /root/tmp/cron.sh
When the cron job is running the ps
outputs like this: 当cron作业正在运行时, ps
输出如下:
PPID PID PGID SID COMMAND
15486 15487 15487 15487 /bin/sh -c /root/tmp/cron.sh
15487 15488 15487 15487 /bin/bash /root/tmp/cron.sh
15488 15489 15489 15487 sleep 888
15488 15490 15490 15487 sudo sleep 999
15490 15494 15490 15487 sleep 999
So the sudo
(and its child) is running in a separate pgrp and the pgid is not the pid of the cron.sh
so my solution ( kill -INT -$$
) would not work. 所以sudo
(及其子)在单独的PGRP正在运行,并且PGID不是的PID cron.sh
所以我的解决方案( kill -INT -$$
)是行不通的。
Then I think we can solve the problem like this: 然后,我认为我们可以解决以下问题:
#!/bin/bash
set -m
sudo sleep 999 & # run sudo in backgroup
pid=$! # save the pid which is also the pgid
sleep 5
sudo kill -INT -$pid # kill the pgrp.
# Use sudo since we're killing root's processes
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.