Golang Non Blocking os.Stdin Read for Cmd

Golang Non Blocking os.Stdin Read for Cmd

提问人:404 提问时间:8/15/2023 更新时间:8/15/2023 访问量:113

问:

cmd := exec.Command("env")
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout

无论命令在读取时不中继,这都很好用envstdin

然而,对于

cmd := exec.Command("env")
reader, writer := os.Pipe()
go func() {
  io.Copy(writer, os.Stdin)
  reader.Close()
}()
cmd.Stdin = reader
cmd.Stdout = os.Stdout

它很好地执行命令,但接下来会阻止,直到进行任何输入(无论是否不需要它)envenv

即使这样也会重现这种情况

cmd := exec.Command("env")
cmd.Stdin = struct{io.Reader}{os.Stdin}
cmd.Stdout = os.Stdout

但这实际上在 Cmd 实现的引擎盖下再现了 Pipe 流,一旦 Stdin 不是 *os,它就会自己。文件(见代码 )

    if f, ok := c.Stdin.(*os.File); ok {
        return f, nil
    }

    pr, pw, err := os.Pipe()
    if err != nil {
        return nil, err
    }
    ...

所以 wat 是这里作为 io 的基本区别。Copy 它应该以同样的方式运行

Copy copies from src to dst until either EOF is reached on src or an error occurs

那么直接提供操作系统的区别是什么。Stdin 将其管道连接到中级阅读器上。以及如何使管道方法以相同的非阻塞方式运行

我的实际情况更加复杂,因为它将 stdin 管道输送到我希望的静止器上。对所描述的行为的理解将为我提供相同的解决方案线索。net.Conn

感谢。

标准丁 非阻塞

评论

1赞 JimB 8/15/2023
当您使用文件描述符直接传递给子进程时,如果不是普通文件,则 exec 包需要创建一个 goroutine 来通过管道传输数据,类似于您的示例。不同之处在于该命令接收一个可以直接检查的 tty。如果您从 fifo 重定向父 go 进程 stdin,您也会得到相同的阻塞行为。os.Stdincmd.Stdinos.Pipe
0赞 404 8/16/2023
@JimB 谢谢。在非常高的层面上,我可以看到你的想法。但是你能澄清一下吗?是 Cmd 实现的问题还是下属 shell 命令的问题?一旦似乎根本不需要 stdin 检查 tty 的原因是什么?为什么在命令关闭后不只是关闭 stdin,而不管它是否是 tty?也许 Cmd 代码中有一行可以指向我以便更好地理解?感谢env
0赞 JimB 8/16/2023
该行为与 Go 或 exec 包没有直接关系,大多数可以通过 stdin 接受数据的非交互式程序会通过检查其 stdin 是什么来检查是否有任何内容要读取。如果你想通过 stdin 接受数据,并且它是一个管道,你必须读取,直到你得到你期望的数据或管道被关闭。在 shell 中运行,它也会阻塞。env < fifo

答: 暂无答案