【问题标题】:Reading from stdout pipe once ready in golang在 golang 中准备好后从标准输出管道读取
【发布时间】:2018-04-03 16:47:28
【问题描述】:

我正面临一个奇怪的 golang 问题。下面的代码将澄清:

package main

import (
    "os/exec"
    "io"
    "fmt"
    "os"
)

var (
    pw io.WriteCloser
    pr io.ReadCloser
)

func main() {
    term := exec.Command("/bin/sh")

    // Get stdin writer pipe
    pw, _ = term.StdinPipe()
    pr, _ = term.StdoutPipe()

    term.Start()

    run("cd ~")
    pwd := run("pwd");

    // Do something with pwd output
    ...

    term.Wait()
}

func run(c string) string {
    io.WriteString(pw, fmt.Sprintln(c))

    buf := make([]byte, 32 * 1024)
    pr.Read(buf)
    return string(buf)
}

我想在 shell 环境中运行一些命令并读取它们的输出。写入/运行命令没有问题,但阅读时似乎有一些限制:

  • 您无法知道命令是否不输出任何内容;
  • 无法检查 stdout 是否已准备好读取。

pr.Read(dest) 方法将阻塞代码流,直到从标准输出中读取某些内容。如前所述,目标是顺序读取(不使用 go 例程和/或无限循环)。这意味着如果我们发送一个cd 命令,函数的终点永远不会到达。

在标准输出文件描述符上通过unix.SetNonblock 设置非块标志似乎可以解决上述问题,但您无法事先知道它是否准备好,并且从@987654325 返回“资源暂时不可用”的错误@调用。

【问题讨论】:

  • 你为什么不直接运行你真正想运行的命令,而不是尝试启动一个shell?
  • 不使用goroutine的原因是什么?
  • @MichaelHampton 不幸的是,一旦您终止进程(shell),您将无法检索当前的工作路径,并且 shell 会以二进制路径作为工作路径重新启动。
  • 你说的功能是什么?该示例显示了一个函数main。在任何情况下,使用 goroutines 都不会阻止子进程 stdout 的顺序读取。
  • 整个方法没有任何意义。你到底想做什么?

标签: go stdout stdin


【解决方案1】:

正如 Cerise Limón 提到的那样,go 函数可能是这里的方法,因为这类交互式脚本练习传统上是用期望完成的。

您可以将并行执行包装到一个库中,它可能仍然看起来像顺序代码,所以这可能会有所帮助:https://github.com/ThomasRooney/gexpect

来自自述文件:

child, err := gexpect.Spawn("python")
if err != nil { panic(err) }
child.Expect(">>>")
child.SendLine("print 'Hello World'")
child.Interact()
child.Close()

【讨论】: