这是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并查看脚本进程是否在中断之前存在,并且只有父进程在中断之后消失,而子进程一直持续到其运行过程,可以证实这一点。

尝试解决...
  • Shell选项在这里仅是一个因素,因为父级运行的set -bm的非交互式调用(这要求子级的PGID与父级的PGID不同-向前相关)。除此之外,两个脚本仅显示
    已启用选项hB,无论是否以交互方式运行。
  • 通过男子重击寻找线索,但没有发现任何帮助。
  • 尝试了一些网络搜索,其中包括来自
    stackoverflow,但是尽管有些类似于此问题,但没有
    我们是一样的。最接近的答案是……
  • 使用等待获取子进程ID并对其调用调用kill-导致“/parent.sh:第30行:kill:(17955)-不允许操作”
  • 调用进程组上的kill-导致“〜/parent.sh:第31行:kill:(-15227)-不允许操作”(使用child的PGID进行kill,由于非交互,因此与parent不同)启用作业控制)
  • 循环通过当前作业并杀死每个

  • 这些解决方案的问题是父级以常规用户身份运行,而子级通过sudo以root身份运行(最终将是二进制文件,而不是suid脚本),因此父级无法杀死它吗?如果那是“不允许操作”的意思,那么当通过终端发送击键中断时,为什么sudo调用的进程会被杀死?

    自然的做法是除非有必要,否则避免使用额外的代码-即,由于脚本在交互运行时会正确运行,因此,在可行的情况下,更可取的是,在非交互运行或通过cron运行时,简单地应用相同的行为。

    最重要的问题是,如何使非交互式运行时发出的中断(或术语)信号产生与交互式运行时发出的中断信号相同的行为?

    谢谢。任何帮助是极大的赞赏。

    最佳答案

  • 当您从交互式 shell 程序(通常在pty上运行)手动运行脚本时,终端驱动程序会捕获CTRL-C并将其转换为SIGINT并发送给前景进程组(脚本本身和sudo)中的所有进程命令)。
  • 当您的脚本从cron运行时,您仅将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/

    10-16 23:15