这是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
这是child.sh:
#!/bin/bash
test -f out.txt && rm out.txt
for second in {1..10}; do
echo "$second" >> out.txt
sleep 1
done
如果在这样的终端中运行父脚本...
~/parent.sh
...大约3秒钟后,通过按键发出一个中断。几秒钟后检查out.txt时,它看起来像是...
1
2
3
...因此指示 parent 和 child 在(按键)中断后结束。通过实时检查
ps -ef
并查看脚本进程在中断之前存在并在中断之后消失来证实这一点。如果像cron这样调用父脚本,则...
* * * * * ~/parent.sh
... out.txt的内容始终是...
1
2
3
4
5
6
7
8
9
10
...因此表明至少 child 没有在(kill命令)中断后结束。通过实时检查
ps -ef
并查看脚本进程是否在中断之前存在,并且只有父进程在中断之后消失,而子进程一直持续到其运行过程,可以证实这一点。尝试解决...
set -bm
的非交互式调用(这要求子级的PGID与父级的PGID不同-向前相关)。除此之外,两个脚本仅显示已启用选项hB,无论是否以交互方式运行。
stackoverflow,但是尽管有些类似于此问题,但没有
我们是一样的。最接近的答案是……
这些解决方案的问题是父级以常规用户身份运行,而子级通过sudo以root身份运行(最终将是二进制文件,而不是suid脚本),因此父级无法杀死它吗?如果那是“不允许操作”的意思,那么当通过终端发送击键中断时,为什么sudo调用的进程会被杀死?
自然的做法是除非有必要,否则避免使用额外的代码-即,由于脚本在交互运行时会正确运行,因此,在可行的情况下,更可取的是,在非交互运行或通过cron运行时,简单地应用相同的行为。
最重要的问题是,如何使非交互式运行时发出的中断(或术语)信号产生与交互式运行时发出的中断信号相同的行为?
谢谢。任何帮助是极大的赞赏。
最佳答案
CTRL-C
并将其转换为SIGINT
并发送给前景进程组(脚本本身和sudo
)中的所有进程命令)。 SIGINT
发送到shell脚本本身,并且sudo
命令将继续运行,并且bash在这种情况下退出时不会杀死其子级。 要将信号显式发送到整个过程组,可以使用否定的过程组ID。对于您的情况,pgid应该是shell脚本的PID,因此请尝试如下操作:
trap 'kill -SIGINT -$$' SIGCHLD
更新:
事实证明,我对pgid的值的假设是错误的。刚用这个简单的
cron.sh
做了一个测试:#!/bin/bash
set -m
sleep 888 &
sudo sleep 999
和
crontal -l
看起来像这样:30 * * * * /root/tmp/cron.sh
当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
因此,
sudo
(及其子级)在单独的pgrp中运行,而pgid不是cron.sh
的pid,因此我的解决方案(kill -INT -$$
)无法正常工作。然后,我认为我们可以解决以下问题:
#!/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
关于linux - 当父级以交互方式/由终端调用时,bash子脚本与父脚本一起退出,但在非交互/由cron调用时,则不存在,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/41234093/