当父以交互方式/通过终端调用父进程时,bash子脚本与父脚本一起退出,但在非交互式/通过cron调用时则不会

时间:2016-12-20 02:45:22

标签: linux bash shell

这是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  

...因此表明父母和孩子结束(击键)中断。通过实时检查ps -ef并查看脚本进程在中断之前存在并在中断之后消失来证实这一点。

如果cron调用父脚本,那么......

* * * * * ~/parent.sh  

... out.txt的内容总是......

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  

...因此表明至少孩子没有结束(kill命令)中断。通过实时检查ps -ef并在中断之前看到脚本进程存在并且在中断之后只有父进程消失来证实这一点,但是子进程一直持续到它运行它的过程为止。

试图解决......

  1. Shell选项只能是一个因素,因为父运行set -bm的非交互式调用(这需要孩子的PGID与父级的PGID不同 - 相关提前)。除此之外,两个脚本仅显示 选项hB已启用,无论是否以交互方式运行。
  2. 通过男人的抨击寻找线索,但没有发现任何帮助。
  3. 尝试了一些网络搜索,其中包含了很多结果 stackoverflow,但有些类似于这个问题,没有 我们是一样的。最接近的答案需要......
    • 使用wait来获取子进程id并对其调用kill - 结果为" /parent.sh:line 30:kill:(17955) - 不允许操作"
    • 在进程组上调用kill - 结果为"〜/ parent.sh:第31行:kill:( - 15227) - 不允许操作" (使用子级的PGID进行杀死,由于启用了作业控制,因此在非交互式时与父级不同)
    • 通过当前工作循环并杀死每个
  4. 这些解决方案的问题是父作为普通用户运行,而子进程通过sudo以root身份运行(它最终将是二进制文件,而不是suid脚本),所以父进程无法杀死它?如果那是"操作不允许"意思是,为什么sudo在通过终端发送击键中断时调用了进程可用?

    自然过程是为了避免额外的代码,除非必要 - 即,因为脚本在交互式运行时表现正确,如果可行的话,在非交互式/通过cron运行时简单地应用相同的行为是非常优选的。 / p>

    最重要的问题是,在非交互式运行时发出中断(或术语)信号可以做什么,产生与交互运行时发出的中断信号相同的行为?

    感谢。非常感谢任何帮助。

1 个答案:

答案 0 :(得分:2)

  1. 当您从交互式shell (通常在 pty 上运行)手动运行脚本时,终端驱动程序会捕获{{> 1}}并将其转换为CTRL-C并发送到 前台进程组 中的所有进程(脚本本身和SIGINT命令)。
  2. 当您的脚本从cron运行时,您只将sudo发送到shell脚本本身,SIGINT命令将继续运行,并且bash在退出此类场景时不会终止其子项。
  3. 要明确向整个流程组发送信号,您可以使用否定的流程组ID 对于你的情况,pgid应该是shell脚本的PID,所以试试这样:

    sudo

    更新

    事实证明我对pgid价值的假设是错误的。刚用这个简单的trap 'kill -SIGINT -$$' SIGCHLD 进行了测试:

    cron.sh

    #!/bin/bash set -m sleep 888 & sudo sleep 999 看起来像这样:

    crontal -l

    当cron作业正在运行30 * * * * /root/tmp/cron.sh 输出时,如下所示:

    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 (和它的孩子)在一个单独的pgrp中运行而pgid不是sudo的pid所以我的解决方案(cron.sh)不起作用。< / p>

    然后我认为我们可以解决这个问题:

    kill -INT -$$