命令 StdinPipe 关闭得太早

Command StdinPipe closes too soon

我一直在尝试使用 exec.Command 调用 pg_restore 并向 StdinPipe 提供来自数据库转储文件的数据,它适用于 1Mb 以下的小文件,但无法处理更大的转储出现 write |1: broken pipe 错误。我还尝试逐行扫描并写入管道,但它导致了相同的错误,并且 运行 像 cmd.Run() 在单独的 goroutine 中也没有帮助。

进行:1.14 OS: macOS

cmd := exec.Command("pg_restore", "--clean", "-n public", "--dbname=DB_URI")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
pw, err := cmd.StdinPipe()
defer pw.Close()
...

done := make(chan struct{})
errCh := make(chan error)
file, err := os.Open("dumpfile")
defer file.Close()

if err := cmd.Start(); err != nil {
    return err
}

_, err = io.Copy(pw, file)

我做错了什么或如何保持管道畅通?

当使用 cat 而不是 pg_restore 时,您的代码有效。
另一方面,当使用 head -10 时,我遇到了与您相同的错误,这实际上是预期的。

由于您是在异步模式下启动 cmd,如果 pg_restore 在消耗完其所有 STDIN 之前停止,io.Copy 将遇到此类错误尝试在封闭的管道上写入。

检查您的 pg_restore 命令的状态(最终 return 代码、打印在其 STDERR 上的内容、日志...)以查看是否存在实际错误。

您可以将此错误视为您不应再向此命令提供输入的正常指示。

包括 cmd.Wait() 应该可以解决您的问题,就像您的评论之一中所说的那样。

在我同事的帮助下,我们发现命令参数格式不正确,因为 Go 直接使用系统调用,程序的每个参数必须分开,所以这里 -n public 导致了问题

"pg_restore", "--clean", "-n public", "--dbname=DB_URI"

并且修复也很清楚 – 拆分它们 -npublic

"pg_restore", "--clean", "-n", "public", "--dbname=DB_URI"