在Go中,如何将函数的stdout捕获到字符串中?

And*_*rio 44 stdout go

例如,在Python中,我可以执行以下操作:

realout = sys.stdout
sys.stdout = StringIO.StringIO()
some_function() # prints to stdout get captured in the StringIO object
result = sys.stdout.getvalue()
sys.stdout = realout
Run Code Online (Sandbox Code Playgroud)

你能在Go中这样做吗?

Eva*_*haw 52

我同意你应该使用这些fmt.Fprint功能,如果你可以管理它.但是,如果您不控制正在捕获其输出的代码,则可能没有该选项.

Mostafa的答案有效,但如果你想在没有临时文件的情况下这样做,你可以使用os.Pipe.这是一个相当于Mostafa的例子,其中一些代码受到Go测试包的启发.

package main

import (
    "bytes"
    "fmt"
    "io"
    "os"
)

func print() {
    fmt.Println("output")
}

func main() {
    old := os.Stdout // keep backup of the real stdout
    r, w, _ := os.Pipe()
    os.Stdout = w

    print()

    outC := make(chan string)
    // copy the output in a separate goroutine so printing can't block indefinitely
    go func() {
        var buf bytes.Buffer
        io.Copy(&buf, r)
        outC <- buf.String()
    }()

    // back to normal state
    w.Close()
    os.Stdout = old // restoring the real stdout
    out := <-outC

    // reading our temp stdout
    fmt.Println("previous output:")
    fmt.Print(out)
}
Run Code Online (Sandbox Code Playgroud)

  • 你真的想要/需要在写入管道的任何东西之前启动`io.Copy`*,因为管道的缓冲能力是有限的.移动开始goroutine是微不足道的,完全绕过了问题.例如:http://play.golang.org/p/PNqa5M8zo7 (4认同)
  • 另外,不要忽略来自“os.Pipe”的错误,并且可能使用“defer”来恢复“os.Stdout”,这样任何可能退出该函数的添加代码都不需要担心它。而这对于并发使用来说是非常不安全的。 (2认同)
  • 我把你的逻辑放入一个库中:https://github.com/PumpkinSeed/cage (2认同)

mat*_*tes 16

这个答案类似于以前的答案,但使用io/ioutil看起来更干净.

http://play.golang.org/p/fXpK0ZhXXf

package main

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

func main() {
  rescueStdout := os.Stdout
  r, w, _ := os.Pipe()
  os.Stdout = w

  fmt.Println("Hello, playground") // this gets captured

  w.Close()
  out, _ := ioutil.ReadAll(r)
  os.Stdout = rescueStdout

  fmt.Printf("Captured: %s", out) // prints: Captured: Hello, playground
}
Run Code Online (Sandbox Code Playgroud)

  • 这与接受的答案有同样的错误.写入管道然后从同一个goroutine读回来是一个坏主意; 特别是如果你暗示你的中间"块"可以是任何数量的输出.它不能,管道具有有限的缓冲能力. (3认同)

Mos*_*afa 15

我不推荐这个,但你可以通过改变来实现它os.Stdout.由于此变量属于类型os.File,因此临时输出也应该是文件.

package main

import (
    "fmt"
    "io/ioutil"
    "os"
    "path/filepath"
)

func print() {
    fmt.Println("output")
}

func main() {
    // setting stdout to a file
    fname := filepath.Join(os.TempDir(), "stdout")
    fmt.Println("stdout is now set to", fname)
    old := os.Stdout // keep backup of the real stdout
    temp, _ := os.Create(fname) // create temp file
    os.Stdout = temp

    print()

    // back to normal state
    temp.Close()
    os.Stdout = old // restoring the real stdout

    // reading our temp stdout
    fmt.Println("previous output:")
    out, _ := ioutil.ReadFile(fname)
    fmt.Print(string(out))
}
Run Code Online (Sandbox Code Playgroud)

我不推荐,因为这是太多的黑客攻击,而且在Go中并不是非常惯用.我建议传递io.Writer给函数并将输出写入其中.这是做几乎相同的事情的更好方法.

package main

import (
    "bytes"
    "fmt"
    "io"
    "os"
)

func print(w io.Writer) {
    fmt.Fprintln(w, "output")
}

func main() {
    fmt.Println("print with byes.Buffer:")
    var b bytes.Buffer
    print(&b)
    fmt.Print(b.String())

    fmt.Println("print with os.Stdout:")
    print(os.Stdout)
}
Run Code Online (Sandbox Code Playgroud)