当前位置: 首页 > 知识库问答 >
问题:

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

那宏大
2023-03-14

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

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

试图解决。。。

  • Shell 选项在这里只能是一个因素,因为父运行集 -bm 的非交互式调用(这需要子级的 PGID 与父级的 PGID 不同 - 相关)。除此之外,两个脚本都只显示启用 hB 的选项,无论是否以交互方式运行。
  • 通过男人抨击寻找线索,但没有发现任何帮助。
  • 尝试了一些网络搜索,其中包括许多来自stackoverflow的结果,但是虽然有些与这个问题相似,但没有一个是相同的。最接近的答案需要...
    • 使用 wait 获取子进程 id 并对其调用 kill - 导致“/parent.sh:第 30 行:kill:(17955) - 不允许操作”
    • 在进程组上调用 kill - 导致“~/parent.sh:第 31 行:kill: (-15227) - 不允许操作”(使用子项的 PGID 进行 kill,由于作业控制启用,该操作在非交互时与父级不同)
    • 循环通过当前作业并杀死每个作业

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

    自然的做法是避免额外的代码,除非必要-也就是说,由于脚本在交互运行时行为正确,如果可行,最好在非交互/通过cron运行时应用相同的行为。

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

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

共有1个答案

越姚石
2023-03-14
    < li >当您从交互式shell(通常在pty上运行)中手动运行脚本时,是终端驱动程序捕获< code>CTRL-C并将其转换为< code>SIGINT并发送给前台进程组中的所有进程(脚本本身和< code>sudo命令)。 < li >当您的脚本从cron运行时,您只需将< code>SIGINT发送到shell脚本本身,并且< code>sudo命令将继续运行,bash在这种情况下退出时不会杀死其子级。

要显式地向整个进程组发送信号,您可以使用负进程组ID。<del>对于您的情况,pgid应该是shell脚本的PID,因此请这样尝试:</del>

trap 'kill -SIGINT -$$' SIGCHLD

事实证明,我对pgid值的假设是错误的。刚刚用这个简单的< code>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 因此我的解决方案(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
 类似资料:
  • 我想在一个非交互式脚本中安装JRE1.7。有没有一种方法可以做到这一点,相当于下面的方法?

  • 问题内容: 是否有可能让bash脚本自动处理通常会以默认操作呈现给用户的提示?目前,我正在使用bash脚本调用内部工具,该工具将向用户显示提示(提示输入Y / N)以完成操作,但是我编写的脚本必须完全“放手”,因此我需要一种发送到提示符的方法,以允许程序继续执行。这可能吗? 问题答案: 这不是“自动完成”,而是自动化。一种用于这些事情的常用工具称为Expect。 您也可能只需要通过管道输入就能摆脱

  • start.sh是父脚本。当SIGTERM被捕获时,它会被转发到其子进程的3个。 为什么即使我在任一情况下等待3个pid(子进程)也存在这种差异?为什么我在等待3个pids时需要睡眠?

  • 交互shell从tty读取用户输入。shell默认会读取启动文件,显示提示符和打开任务控制等。用户可以和shell交互。 脚本总是运行在非交互的shell上。同样,脚本可以访问它自己的tty,这使得在脚本中依然可以模拟出交互的shell。 #!/bin/bash MY_PROMPT='$ ' while : do echo -n "$MY_PROMPT" r

  • 问题内容: 我需要确定调用我的Python脚本的外壳是否处于交互模式。如果它处于交互模式,则程序应将输出通过管道传递到less(1)以便于阅读。如果没有,它应该只将其输出打印到stdout,以便将其通过管道传输到打印机,文件或其他寻呼机。 在shell脚本中,我将检查是否定义了提示变量$ PS1,或者在$-变量中存储的标志中寻找-i选项。 从Python内部测试交互性的首选方法是什么? 问题答案:

  • 本文向大家介绍.net与javascript脚本的交互方法总结,包括了.net与javascript脚本的交互方法总结的使用技巧和注意事项,需要的朋友参考一下 本文实例总结了.net与javascript脚本的交互方法,分享给大家供大家参考。具体方法如下: 1.asp.net呼叫js 在这里情况下,你可以调用页面中的JS脚本的函数都可以 2、js脚本如何访问服务器控件的值 界面上有一个TextBo