我有一个Go程序,它使用了来自外壳管道的“实时”输入,例如:
tail -f some/file | my-program
my-program
是使用内置的交互式程序rivo/tview
。我希望能够使用Ctrl-C关闭程序,并使它也终止tail -f
向其提供输入的程序。
当前,我必须按两次Ctrl-C才能回到我的shell提示符。通过按Ctrl-C一次可以返回提示吗?
根据@torek对进度组的说明调整了程序,并观察到我可以使用unix.Getpgid(pid)
以下方式获取进度组ID:
import (
"os"
"golang.org/x/sys/unix"
)
func main() {
// do stuff with piped input
pid := os.Getpid()
pgid, err := unix.Getpgid(pid)
if err != nil {
log.Fatalf("could not get process group id for pid: %v\n", pid)
}
processGroup, err := os.FindProcess(pgid)
if err != nil {
log.Fatalf("could not find process for pid: %v\n", pgid)
}
processGroup.Signal(os.Interrupt)
}
这从我最初的问题中传达了我想要的行为。
syscall
由于发现警告,我选择不使用:
不推荐使用:此程序包已被锁定。调用者应改用golang.org/x/sys信息库中的相应软件包。这也是应该应用新系统或版本所需的更新的地方。有关更多信息,请参见https://golang.org/s/go1.4-syscall。
我计划更新程序,以使用本文概述的策略来检测是否给了管道,因此,当检测到管道时,我将在中断上执行上述进程组信号。
有什么问题吗?
我们将假定使用一个类似Unix的系统,该shell可以理解并参与作业控制(并且它们现在都可以执行)。运行命令时,外壳程序会创建一个称为进程组或“ pgroup”的文件,以容纳组成该命令的每个进程。如果该命令是管道(如前所述),则管道中的每个进程都将获得相同的pgroup-ID(请参阅参考资料setpgid
)。
如果该命令在Forgeground中运行(不带&
),则控制终端将分配此特定的pgid。按压信号生成密钥,如一个CTRL-C或CTRL-\,发送相应的信号(SIGINT
和SIGQUIT
在这些情况下)到PGROUP,使用内部killpg
或等同物。这会将信号发送到pgroup的每个成员。
(对进程进行后台处理只是“咳嗽”,只需将控制tty上的pgid取回,然后重新启动管道中的进程即可。要做到这一点,并不是那么简单,如此处的“重新启动”所示。)
问题的可能根源在于,交互式程序会将控制终端置于cbreak或raw模式,并禁用来自键盘按键的部分或全部信号,因此,例如,CTRL-C不再导致内核的tty模块在以下位置发送信号所有。相反,如果您看到应该引起挂起(CTRL-Z)或终止的键,则该程序必须自行执行挂起或终止。程序员有时会认为这只是暂停或终止而已,但是由于整个管道永远都没有问题信号,所以情况并非如此,除非整个Shell管道仅由交互式程序组成。
解决方法是让程序在对控制终端进行任何必要的清理(临时或永久)后,将信号发送到其自己的pgroup。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句