猿问

从交互式 go cli 中终止 shell 管道

我有一个 Go 程序,它使用来自 shell 管道的“实时”输入,例如:


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。


我计划更新我的程序以使用本文中概述的策略来检测它是否被赋予了管道,因此当检测到管道时,我将在中断时执行上述进程组信号。


有什么问题吗?


拉丁的传说
浏览 99回答 1
1回答

桃花长相依

我们将假设一个类 Unix 系统,使用一个理解并参与作业控制的 shell (现在他们都这样做了)。当您运行命令时,shell 会创建一个称为进程组或“pgroup”的东西来保存构成命令的每个进程。如果命令是管道(就像这个一样),管道中的每个进程都会获得相同的 pgroup-ID(请参阅 参考资料setpgid)。如果命令在前台运行(不带&),则控制终端会分配给它这个特定的 pgid。按下信号生成键之一,例如CTRL-C或CTRL-\,将相应的信号(SIGINT和SIGQUIT在这些情况下)发送到 pgroup,使用内部killpg或等效。这会将信号发送给 pgroup 的每个成员。(后台进程只是简单地*咳嗽*收回控制 tty 上的 pgid,然后重新启动管道中的进程。但是,要做到这一点并不那么简单,正如这里的“重新启动”所示。)这里问题的可能来源是交互式程序会将控制终端置于cbreak或raw模式并禁用来自键盘键的部分或全部信号,这样,例如,CTRL-C不再导致内核的 tty 模块在全部。相反,如果您看到应该导致暂停 ( CTRL-Z) 或终止的键,则程序必须执行自己的暂停或终止。程序员有时认为这只是暂停或终止——但由于整个管道从未收到有问题的信号,所以情况并非如此,除非整个 shell 管道仅由交互式程序组成。解决方法是让程序在对控制终端进行任何必要的清理(临时或永久)之后将信号发送到它自己的 pgroup。
随时随地看视频慕课网APP

相关分类

Go
我要回答