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

标签 shell go

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

tail -f some/file | my-program

my-program 是一个使用 rivo/tview 构建的交互式程序。我希望能够使用 Ctrl-C 关闭我的程序,并让它终止向其提供输入的 tail -f

目前,我必须按 Ctrl-C 两次才能返回 shell 提示符。有什么办法可以通过按 Ctrl-C 一次返回提示符吗?


根据@torek's explanation of progress groups调整了我的程序和 observation that I can get the progress group ID using unix.Getpgid(pid) :

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)
}

这从我最初的问题中得到了我想要的行为。

由于warning I found,我选择不使用syscall :

Deprecated: this package is locked down. Callers should use the corresponding package in the golang.org/x/sys repository instead. That is also where updates required by new systems or versions should be applied. See https://golang.org/s/go1.4-syscall for more information.

我计划更新我的程序以检测是否使用 strategy outlined in this article 为其提供了管道。 ,因此当检测到管道时,我将在中断时执行上述进程组信号。

有什么问题吗?

最佳答案

我们将假设一个类 Unix 系统,使用一个能够理解并参与作业控制的 shell(现在它们都这样做了)。当您运行命令时,shell 会创建一个称为进程组或“pgroup”的东西来保存组成该命令的每个进程。如果命令是一个管道(就像这个),管道中的每个进程都会获得相同的 pgroup-ID(请参阅 setpgid )。

如果命令在forgeground中运行(没有 & ),控制终端就会分配给它这个特定的 pgid 。按信号生成键之一,例如 CTRL-CCTRL-\,将相应的信号(在这些情况下为 SIGINTSIGQUIT)发送到pgroup,使用内部 killpg 或等效的。这会将信号发送给 pgroup 的每个成员。

(后台进程只是*咳嗽*收回控制tty上的pgid,然后重新启动管道中的进程。不过要实现这一点并不那么简单,如此处的“重新启动”所示。)

这里问题的可能根源是交互式程序会将控制终端置于cbreakraw模式并禁用某些或所有来自键盘按键的信号,例如,CTRL-C 不再导致内核的 tty 模块发送信号。相反,如果您看到某个键应该导致挂起 (CTRL-Z) 或终止,则程序必须自行挂起或终止。程序员有时会认为这只是暂停或终止,但由于整个管道从未收到有问题的信号,所以情况并非如此,除非整个 shell 管道仅由交互式程序组成。 p>

解决方法是让程序在对控制终端进行任何必要的清理(临时或永久)后,将信号发送到其自己的 pgroup。

关于shell - 从交互式 go cli 终止 shell 管道,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65515667/

相关文章:

shell - Jenkins:从 shell 脚本设置环境变量

shell - sed 括号表达式和捕获组

compilation - 为什么 "8g foo.go -o foo.8"在 go 语言命令行中对我不起作用?

go - YAML Unmarshal map [string] struct

go - 使用包作为全局变量的存储

go - 数据库连接最佳实践

linux - 在 bash 中重复特定的代码行

Python 子进程无法读取 shell 中的所有输出

php - 使用 PHP 库进行 FFMPEG 音频合并

go - 实现 JSON Marshaler 接口(interface)时获取标签