为什么我不能通过击键消除从 Bash 脚本调用的超时?

为什么我不能通过击键消除从 Bash 脚本调用的超时?

[编辑:这看起来与其他一些询问如何杀死所有生成的进程的问题类似 - 答案似乎都是使用 pkill。所以我的问题的核心可能是:有没有办法将 Ctrl-C/Z 传播到脚本生成的所有进程?]

当使用coreutils 中的命令调用 SoX 时(已rec讨论)timeout这里),一旦从 Bash 脚本中调用它,似乎没有任何方法可以通过击键来杀死它。

例子:

timeout 10 rec test.wav

Ctrl...可以用 bash 中的+CCtrl+杀死Z,但从脚本内部调用它时则不行。

timeout 10 ping nowhere

Ctrl...可以在 bash 中用+CCtrl+杀死Z,从脚本内部运行时用Ctrl+杀死。Z

我可以找到进程 ID 并以这种方式杀死它,但为什么我不能使用标准的中断按键?有什么方法可以构建我的脚本吗?

答案1

Ctrl+等信号键C向前台所有进程发送信号进程组

在典型情况下,进程组就是管道。例如,在 中head <somefile | sort,正在运行的进程head和正在运行的进程sort位于同一个进程组中,shell也是如此,因此它们都接收到信号。当您在后台 ( somecommand &) 运行作业时,该作业位于其自己的进程组中,因此按Ctrl+C不会影响它。

timeout程序将自己置于自己的进程组中。从源代码来看:

/* Ensure we're in our own group so all subprocesses can be killed.
   Note we don't just put the child in a separate group as
   then we would need to worry about foreground and background groups
   and propagating signals between them.  */
setpgid (0, 0);

当发生超时时,timeout执行简单的权宜之计,杀死它所属的进程组。由于它已将自己放入一个单独的进程组中,因此其父进程将不在该组中。此处使用进程组可确保如果子应用程序分叉为多个进程,则其所有进程都将收到信号。

当您timeout直接在命令行上运行并按Ctrl+时,子进程C都会收到生成的 SIGINT ,但其父进程的交互式 shell 不会收到。当从脚本调用时,只有运行脚本的 shell 会收到信号:由于它位于不同的进程组中,因此不会收到信号。timeouttimeouttimeouttimeout

您可以使用内置函数在 shell 脚本中设置信号处理程序trap。不幸的是,事情没那么简单。考虑一下:

#!/bin/sh
trap 'echo Interrupted at $(date)' INT
date
timeout 5 sleep 10
date

如果 2 秒后按Ctrl+ C,仍会等待整整 5 秒,然后打印“Interrupted”消息。这是因为当前台作业处于活动状态时,shell 不会运行陷阱代码。

要解决此问题,请在后台运行该作业。在信号处理程序中,调用kill将信号中继到timeout进程组。

#!/bin/sh
trap 'kill -INT -$pid' INT
timeout 5 sleep 10 &
pid=$!
wait $pid

答案2

以吉尔斯提供的出色答案为基础。超时命令有一个前台选项,如果使用则 CTRL+C 将退出超时命令。

#!/bin/sh
trap 'echo caught interrupt and exiting;exit' INT
date
timeout --foreground 5 sleep 10
date

答案3

基本上Ctrl+C发送一个SIGINT信号,而Ctrl+Z发送一个SIGTSTP信号。

SIGTSTP只是停止该过程,aSIGCONT将继续它。

这适用于在命令行上分叉的前台进程。

如果您的进程是后台进程,您将必须以不同的方式将该信号发送到进程。kill会那么做。理论上,kill 上的“-”运算符也应该向子进程发出信号,但这很少按预期工作。

进一步阅读:Unix 信号

相关内容