[编辑:这看起来与其他一些询问如何杀死所有生成的进程的问题类似 - 答案似乎都是使用 pkill。所以我的问题的核心可能是:有没有办法将 Ctrl-C/Z 传播到脚本生成的所有进程?]
当使用coreutils 中的命令调用 SoX 时(已rec
讨论)timeout
这里),一旦从 Bash 脚本中调用它,似乎没有任何方法可以通过击键来杀死它。
例子:
timeout 10 rec test.wav
Ctrl...可以用 bash 中的+C或Ctrl+杀死Z,但从脚本内部调用它时则不行。
timeout 10 ping nowhere
Ctrl...可以在 bash 中用+C或Ctrl+杀死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 会收到信号:由于它位于不同的进程组中,因此不会收到信号。timeout
timeout
timeout
timeout
您可以使用内置函数在 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 信号